[IMP] search view

bzr revid: xmo@openerp.com-20120522132442-am276368d5nkwn2k
This commit is contained in:
Xavier Morel 2012-05-22 15:24:42 +02:00
commit b23e1ed416
23 changed files with 2907 additions and 4527 deletions

View File

@ -36,19 +36,6 @@
"static/lib/underscore/underscore.string.js",
"static/lib/backbone/backbone.js",
"static/lib/visualsearch/lib/js/visualsearch.js",
"static/lib/visualsearch/lib/js/utils/backbone_extensions.js",
"static/lib/visualsearch/lib/js/utils/hotkeys.js",
"static/lib/visualsearch/lib/js/utils/inflector.js",
"static/lib/visualsearch/lib/js/utils/jquery_extensions.js",
"static/lib/visualsearch/lib/js/utils/search_parser.js",
"static/lib/visualsearch/lib/js/models/search_facets.js",
"static/lib/visualsearch/lib/js/models/search_query.js",
"static/lib/visualsearch/lib/js/templates/templates.js",
"static/lib/visualsearch/lib/js/views/search_facet.js",
"static/lib/visualsearch/lib/js/views/search_input.js",
"static/lib/visualsearch/lib/js/views/search_box.js",
"static/lib/labjs/LAB.src.js",
"static/lib/py.js/lib/py.js",
"static/src/js/boot.js",
@ -74,9 +61,6 @@
"static/lib/jquery.ui.timepicker/css/jquery-ui-timepicker-addon.css",
"static/lib/jquery.ui.notify/css/ui.notify.css",
"static/lib/jquery.tipsy/tipsy.css",
"static/lib/visualsearch/lib/css/reset.css",
"static/lib/visualsearch/lib/css/workspace.css",
"static/lib/visualsearch/lib/css/icons.css",
# "static/src/css/base_old.css",
"static/src/css/base.css",
"static/src/css/data_export.css",

View File

@ -1287,25 +1287,6 @@ class SearchView(View):
del filter['domain']
return filters
@openerpweb.jsonrequest
def save_filter(self, req, model, name, context_to_save, domain):
Model = req.session.model("ir.filters")
ctx = common.nonliterals.CompoundContext(context_to_save)
ctx.session = req.session
ctx = ctx.evaluate()
domain = common.nonliterals.CompoundDomain(domain)
domain.session = req.session
domain = domain.evaluate()
uid = req.session._uid
context = req.session.eval_context(req.context)
to_return = Model.create_or_replace({"context": ctx,
"domain": domain,
"model_id": model,
"name": name,
"user_id": uid
}, context)
return to_return
@openerpweb.jsonrequest
def add_to_dashboard(self, req, menu_id, action_id, context_to_save, domain, view_mode, name=''):
ctx = common.nonliterals.CompoundContext(context_to_save)

View File

@ -1,22 +0,0 @@
Copyright (c) 2011 Samuel Clay, @samuelclay, DocumentCloud
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,16 +0,0 @@
__ ___ _ _____ _ _
\ \ / (_) | |/ ____| | | (_)
\ \ / / _ ___ _ _ __ _| | (___ ___ __ _ _ __ ___| |__ _ ___
\ \/ / | / __| | | |/ _` | |\___ \ / _ \/ _` | '__/ __| '_ \ | / __|
\ / | \__ \ |_| | (_| | |____) | __/ (_| | | | (__| | | |_| \__ \
\/ |_|___/\__,_|\__,_|_|_____/ \___|\__,_|_| \___|_| |_(_) |___/
_/ |
|__/
VisualSearch.js enhances ordinary search boxes with the ability to autocomplete
faceted search queries. Specify the facets for completion, along with the
completable values for any facet. You can retrieve the search query as a
structured object, so you don't have to parse the query string yourself.
For documentation, pre-packed downloads, demos, and tests, see:
http://documentcloud.github.com/visualsearch

View File

@ -1,37 +0,0 @@
require 'rubygems'
require 'jammit'
require 'fileutils'
desc "Use Jammit to compile the multiple versions of Visual Search"
task :build do
$VS_MIN = false
Jammit.package!({
:config_path => "assets.yml",
:output_folder => "build"
})
$VS_MIN = true
Jammit.package!({
:config_path => "assets.yml",
:output_folder => "build-min"
})
# Move the JSTs back to lib to accomodate the demo page.
FileUtils.mv("build/visualsearch_templates.js", "lib/js/templates/templates.js")
# Fix image url paths.
['build', 'build-min'].each do |build|
File.open("#{build}/visualsearch.css", 'r+') do |file|
css = file.read
css.gsub!(/url\((.*?)images\/embed\/icons/, 'url(../images/embed/icons')
file.rewind
file.write(css)
file.truncate(css.length)
end
end
end
desc "Build the docco documentation"
task :docs do
sh "docco lib/js/*.js lib/js/**/*.js"
end

View File

@ -1,28 +0,0 @@
embed_assets: datauri
javascript_compressor: closure
template_function: _.template
gzip_assets: <% if $VS_MIN %>on<% else %>off<% end %>
compress_assets: <% if $VS_MIN %>on<% else %>off<% end %>
javascripts:
dependencies:
- vendor/jquery-*.js
- vendor/jquery.ui.core.js
- vendor/jquery.ui.widget.js
- vendor/jquery.ui.position.js
- vendor/jquery.ui.*.js
- vendor/underscore-*.js
- vendor/backbone-*.js
visualsearch:
- lib/js/visualsearch.js
- lib/js/views/*.js
- lib/js/utils/*.js
- lib/js/models/*.js
- lib/js/templates/*.jst
<% unless $VS_MIN %>visualsearch_templates:
- lib/js/templates/*.jst
<% end %>
stylesheets:
visualsearch:
- lib/css/*.css

View File

@ -1,310 +0,0 @@
.VS-search .VS-icon {
background-repeat: no-repeat;
background-position: center center;
vertical-align: middle;
width: 16px; height: 16px;
}
.VS-search .VS-icon-cancel {
width: 11px; height: 11px;
background-position: center 0;
background-image: url("data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAWCAYAAAAW5GZjAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAb9JREFUeNqUUr1qAkEQ3j0khQp6kihaeGgEEa18gTQR0iRY+BaBSMDGwidIEUKqFL6BopgqBAJ5AMFGjUU0d4WHEvwJarvZ77gRIzGYgb1hZr+Z75vZ40IIzqTNZrPj8Xicn0wmmcViEXS73aaqqq+BQODG6/W+A8MBNk3zfDAY3C6Xy0O2ZS6X6zMSiVwHg8FHLjtq7Xb7RQKj7BeTzVCgJ5PJU2U0GhUk7REuMpkMi8fjFggeMeecrVYrFRId0CgTAgDDMFg4HLbA8IjJgHNgGEr0er0fQIphUmZAwdSUADUB4RFDsz3oSMF6CLzZkQqgGebz+Z75dDqNdTqdp13bgDmdTj2VSp0oWHg0Gr2UNH2Z/9o+yMv7K4/HY/C/XhDUfr//jl7QQVT9fp/V63VWqVRYt9tliUSCZbPZg1wux9Lp9PqFeK1Wu9A0DdXz7YM87i0FrVZLs4Fi1wmFQh/NZjOmVKvVgq7rR/QflMtlixGedjwcDlUpMQ9tbzalkAAB2/R297mNW+sT2wUbUnA//V/nYrH4QOBNABUQuFQq3TNMuc82sDVrz41G42yvPeODAwZQ0QzwiJEnzLcAAwBJ6WXlwoBgZAAAAABJRU5ErkJggg==");
cursor: pointer;
}
.VS-search .VS-icon-cancel:hover {
background-position: center -11px;
}
.VS-search .VS-icon-search {
width: 12px; height: 12px;
background-image: url("data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAUZJREFUeNpUUM2qgmAQzS8NiUgLzTIXLZQW1QuI9AY9QPSW9gQ9QiriwpJQEBVrVWT2d7p2L9xZzDdzZs7M+YYqy/J8Ptu2vd/v4zgeDAaqqk4mE47jar9GnU6nzWbjOA5FUa/Xq0Jns9l8Pud5vkpp58cwAOzhcBhFkeu6GNztdg3D+Db5vo9nOp2iiWGYTqdDCMFe4LquI0aVpGmKR9M0lmUbjQY8YiBJklTb4YkoilBzOBzq9TogeMQIJEmqmlAlo9EIyXa7tSyrKAp4xEBkWUb5q2k8Hh+PR8/zwjCEgufz+aESstvtoKnVan2GgY31kBkEAfT1ej1FUZDiNIIgrFYr9H1ug3teLpfH43G/3/FBUJGu1+s8z8FZLpc0mmiabrfbf5fEumazuVgsTNO8Xq+3242qRNT+G0CMz7IMzH6//xZgAA60tj6rqzxpAAAAAElFTkSuQmCC");
}
/*------------------------------ RESET + DEFAULT STYLES ---------------------------------*/
/*
Eric Meyer's final reset.css
Source: http://meyerweb.com/eric/thoughts/2007/05/01/reset-reloaded/
*/
.VS-search div, .VS-search span, .VS-search a, .VS-search img,
.VS-search ul, .VS-search li, .VS-search form, .VS-search label,
.VS-interface ul, .VS-interface li, .VS-interface {
margin: 0;
padding: 0;
border: 0;
outline: 0;
font-weight: inherit;
font-style: inherit;
font-size: 100%;
font-family: inherit;
vertical-align: baseline;
}
.VS-search :focus {
outline: 0;
}
.VS-search {
line-height: 1;
color: black;
}
.VS-search ol, .VS-search ul {
list-style: none;
}
/* ===================== */
/* = General and Reset = */
/* ===================== */
.VS-search {
font-family: Arial, sans-serif;
color: #373737;
font-size: 12px;
}
.VS-search input {
display: block;
border: none;
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
outline: none;
margin: 0; padding: 4px;
background: transparent;
font-size: 16px;
line-height: 20px;
width: 100%;
}
.VS-interface, .VS-search .dialog, .VS-search input {
font-family: "Lucida Grande", "Lucida Sans Unicode", Helvetica, Arial, sans-serif !important;
line-height: 1.1em;
}
/* ========== */
/* = Layout = */
/* ========== */
.VS-search .VS-search-box {
cursor: text;
position: relative;
background: transparent;
border: 2px solid #ccc;
border-radius: 16px; -webkit-border-radius: 16px; -moz-border-radius: 16px;
background-color: #fafafa;
-webkit-box-shadow: inset 0px 0px 3px #ccc;
-moz-box-shadow: inset 0px 0px 3px #ccc;
box-shadow: inset 0px 0px 3px #ccc;
min-height: 28px;
height: auto;
}
.VS-search .VS-search-box.VS-focus {
border-color: #acf;
-webkit-box-shadow: inset 0px 0px 3px #acf;
-moz-box-shadow: inset 0px 0px 3px #acf;
box-shadow: inset 0px 0px 3px #acf;
}
.VS-search .VS-search-inner {
position: relative;
margin: 0 20px 0 22px;
overflow: hidden;
}
.VS-search input {
width: 100px;
}
.VS-search input,
.VS-search .VS-input-width-tester {
padding: 6px 0;
float: left;
color: #808080;
font: 13px/17px Helvetica, Arial;
}
.VS-search.VS-focus input {
color: #606060;
}
.VS-search .VS-icon-search {
position: absolute;
left: 9px; top: 8px;
}
.VS-search .VS-icon-cancel {
position: absolute;
right: 9px; top: 8px;
}
/* ================ */
/* = Search Facet = */
/* ================ */
.VS-search .search_facet {
float: left;
margin: 0;
padding: 0 0 0 14px;
position: relative;
border: 1px solid transparent;
height: 20px;
margin: 3px -3px 3px 0;
}
.VS-search .search_facet.is_selected {
margin-left: -3px;
-webkit-border-radius: 16px;
-moz-border-radius: 16px;
border-radius: 16px;
background-color: #d2e6fd;
background-image: -moz-linear-gradient(top, #d2e6fd, #b0d1f9); /* FF3.6 */
background-image: -webkit-gradient(linear, left top, left bottom, from(#d2e6fd), to(#b0d1f9)); /* Saf4+, Chrome */
background-image: linear-gradient(top, #d2e6fd, #b0d1f9);
border: 1px solid #6eadf5;
}
.VS-search .search_facet .category {
float: left;
text-transform: uppercase;
font-weight: bold;
font-size: 10px;
color: #808080;
padding: 8px 0 5px;
line-height: 13px;
cursor: pointer;
padding: 4px 0 0;
}
.VS-search .search_facet.is_selected .category {
margin-left: 3px;
}
.VS-search .search_facet .search_facet_input_container {
float: left;
}
.VS-search .search_facet input {
margin: 0;
padding: 0;
color: #000;
font-size: 13px;
line-height: 16px;
padding: 5px 0 5px 4px;
height: 16px;
width: auto;
z-index: 100;
position: relative;
padding-top: 1px;
padding-bottom: 2px;
padding-right: 3px;
}
.VS-search .search_facet.is_editing input,
.VS-search .search_facet.is_selected input {
color: #000;
}
.VS-search .search_facet .search_facet_remove {
position: absolute;
left: 0;
top: 4px;
}
.VS-search .search_facet.is_selected .search_facet_remove {
opacity: 0.4;
left: 3px;
filter: alpha(opacity=40);
background-position: center -11px;
}
.VS-search .search_facet .search_facet_remove:hover {
opacity: 1;
}
.VS-search .search_facet.is_editing .category,
.VS-search .search_facet.is_selected .category {
color: #000;
}
.VS-search .search_facet.search_facet_maybe_delete .category,
.VS-search .search_facet.search_facet_maybe_delete input {
color: darkred;
}
/* ================ */
/* = Search Input = */
/* ================ */
.VS-search .search_input {
height: 28px;
float: left;
margin-left: -1px;
}
.VS-search .search_input input {
padding: 6px 3px 6px 2px;
line-height: 10px;
height: 22px;
margin-top: -4px;
width: 10px;
z-index: 100;
min-width: 4px;
position: relative;
}
.VS-search .search_input.is_editing input {
color: #202020;
}
/* ================ */
/* = Autocomplete = */
/* ================ */
.VS-interface.ui-autocomplete {
position: absolute;
border: 1px solid #C0C0C0;
border-top: 1px solid #D9D9D9;
background-color: #F6F6F6;
cursor: pointer;
z-index: 10000;
padding: 0;
margin: 0;
width: auto;
min-width: 80px;
max-width: 220px;
max-height: 240px;
overflow-y: auto;
overflow-x: hidden;
font-size: 13px;
top: 5px;
opacity: 0.97;
box-shadow: 3px 4px 5px -2px rgba(0, 0, 0, 0.5); -webkit-box-shadow: 3px 4px 5px -2px rgba(0, 0, 0, 0.5); -moz-box-shadow: 3px 4px 5px -2px rgba(0, 0, 0, 0.5);
}
.VS-interface.ui-autocomplete .ui-autocomplete-category {
text-transform: capitalize;
font-size: 11px;
padding: 4px 4px 4px;
border-top: 1px solid #A2A2A2;
border-bottom: 1px solid #A2A2A2;
background-color: #B7B7B7;
text-shadow: 0 -1px 0 #999;
font-weight: bold;
color: white;
cursor: default;
}
.VS-interface.ui-autocomplete .ui-menu-item {
float: none;
}
.VS-interface.ui-autocomplete .ui-menu-item a {
color: #000;
outline: none;
display: block;
padding: 3px 4px 5px;
border-radius: none;
line-height: 1;
background-color: #F8F8F8;
background-image: -moz-linear-gradient(top, #F8F8F8, #F3F3F3); /* FF3.6 */
background-image: -webkit-gradient(linear, left top, left bottom, from(#F8F8F8), to(#F3F3F3)); /* Saf4+, Chrome */
background-image: linear-gradient(top, #F8F8F8, #F3F3F3);
border-top: 1px solid #FAFAFA;
border-bottom: 1px solid #f0f0f0;
}
.VS-interface.ui-autocomplete .ui-menu-item a:active {
outline: none;
}
.VS-interface.ui-autocomplete .ui-menu-item .ui-state-hover {
background-color: #6483F7;
background-image: -moz-linear-gradient(top, #648bF5, #2465f3); /* FF3.6 */
background-image: -webkit-gradient(linear, left top, left bottom, from(#648bF5), to(#2465f3)); /* Saf4+, Chrome */
background-image: linear-gradient(top, #648bF5, #2465f3);
border-top: 1px solid #5b83ec;
border-bottom: 1px solid #1459e9;
border-left: none;
border-right: none;
color: white;
margin: 0;
}
.VS-interface.ui-autocomplete .ui-corner-all {
border-radius: 0;
}
.VS-interface.ui-autocomplete li {
list-style: none;
width: auto;
}

View File

@ -1,310 +0,0 @@
.VS-search .VS-icon {
background-repeat: no-repeat;
background-position: center center;
vertical-align: middle;
width: 16px; height: 16px;
}
.VS-search .VS-icon-cancel {
width: 11px; height: 11px;
background-position: center 0;
background-image: url(../images/embed/icons/cancel_search.png?1311104738);
cursor: pointer;
}
.VS-search .VS-icon-cancel:hover {
background-position: center -11px;
}
.VS-search .VS-icon-search {
width: 12px; height: 12px;
background-image: url(../images/embed/icons/search_glyph.png?1311104738);
}
/*------------------------------ RESET + DEFAULT STYLES ---------------------------------*/
/*
Eric Meyer's final reset.css
Source: http://meyerweb.com/eric/thoughts/2007/05/01/reset-reloaded/
*/
.VS-search div, .VS-search span, .VS-search a, .VS-search img,
.VS-search ul, .VS-search li, .VS-search form, .VS-search label,
.VS-interface ul, .VS-interface li, .VS-interface {
margin: 0;
padding: 0;
border: 0;
outline: 0;
font-weight: inherit;
font-style: inherit;
font-size: 100%;
font-family: inherit;
vertical-align: baseline;
}
.VS-search :focus {
outline: 0;
}
.VS-search {
line-height: 1;
color: black;
}
.VS-search ol, .VS-search ul {
list-style: none;
}
/* ===================== */
/* = General and Reset = */
/* ===================== */
.VS-search {
font-family: Arial, sans-serif;
color: #373737;
font-size: 12px;
}
.VS-search input {
display: block;
border: none;
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
outline: none;
margin: 0; padding: 4px;
background: transparent;
font-size: 16px;
line-height: 20px;
width: 100%;
}
.VS-interface, .VS-search .dialog, .VS-search input {
font-family: "Lucida Grande", "Lucida Sans Unicode", Helvetica, Arial, sans-serif !important;
line-height: 1.1em;
}
/* ========== */
/* = Layout = */
/* ========== */
.VS-search .VS-search-box {
cursor: text;
position: relative;
background: transparent;
border: 2px solid #ccc;
border-radius: 16px; -webkit-border-radius: 16px; -moz-border-radius: 16px;
background-color: #fafafa;
-webkit-box-shadow: inset 0px 0px 3px #ccc;
-moz-box-shadow: inset 0px 0px 3px #ccc;
box-shadow: inset 0px 0px 3px #ccc;
min-height: 28px;
height: auto;
}
.VS-search .VS-search-box.VS-focus {
border-color: #acf;
-webkit-box-shadow: inset 0px 0px 3px #acf;
-moz-box-shadow: inset 0px 0px 3px #acf;
box-shadow: inset 0px 0px 3px #acf;
}
.VS-search .VS-search-inner {
position: relative;
margin: 0 20px 0 22px;
overflow: hidden;
}
.VS-search input {
width: 100px;
}
.VS-search input,
.VS-search .VS-input-width-tester {
padding: 6px 0;
float: left;
color: #808080;
font: 13px/17px Helvetica, Arial;
}
.VS-search.VS-focus input {
color: #606060;
}
.VS-search .VS-icon-search {
position: absolute;
left: 9px; top: 8px;
}
.VS-search .VS-icon-cancel {
position: absolute;
right: 9px; top: 8px;
}
/* ================ */
/* = Search Facet = */
/* ================ */
.VS-search .search_facet {
float: left;
margin: 0;
padding: 0 0 0 14px;
position: relative;
border: 1px solid transparent;
height: 20px;
margin: 3px -3px 3px 0;
}
.VS-search .search_facet.is_selected {
margin-left: -3px;
-webkit-border-radius: 16px;
-moz-border-radius: 16px;
border-radius: 16px;
background-color: #d2e6fd;
background-image: -moz-linear-gradient(top, #d2e6fd, #b0d1f9); /* FF3.6 */
background-image: -webkit-gradient(linear, left top, left bottom, from(#d2e6fd), to(#b0d1f9)); /* Saf4+, Chrome */
background-image: linear-gradient(top, #d2e6fd, #b0d1f9);
border: 1px solid #6eadf5;
}
.VS-search .search_facet .category {
float: left;
text-transform: uppercase;
font-weight: bold;
font-size: 10px;
color: #808080;
padding: 8px 0 5px;
line-height: 13px;
cursor: pointer;
padding: 4px 0 0;
}
.VS-search .search_facet.is_selected .category {
margin-left: 3px;
}
.VS-search .search_facet .search_facet_input_container {
float: left;
}
.VS-search .search_facet input {
margin: 0;
padding: 0;
color: #000;
font-size: 13px;
line-height: 16px;
padding: 5px 0 5px 4px;
height: 16px;
width: auto;
z-index: 100;
position: relative;
padding-top: 1px;
padding-bottom: 2px;
padding-right: 3px;
}
.VS-search .search_facet.is_editing input,
.VS-search .search_facet.is_selected input {
color: #000;
}
.VS-search .search_facet .search_facet_remove {
position: absolute;
left: 0;
top: 4px;
}
.VS-search .search_facet.is_selected .search_facet_remove {
opacity: 0.4;
left: 3px;
filter: alpha(opacity=40);
background-position: center -11px;
}
.VS-search .search_facet .search_facet_remove:hover {
opacity: 1;
}
.VS-search .search_facet.is_editing .category,
.VS-search .search_facet.is_selected .category {
color: #000;
}
.VS-search .search_facet.search_facet_maybe_delete .category,
.VS-search .search_facet.search_facet_maybe_delete input {
color: darkred;
}
/* ================ */
/* = Search Input = */
/* ================ */
.VS-search .search_input {
height: 28px;
float: left;
margin-left: -1px;
}
.VS-search .search_input input {
padding: 6px 3px 6px 2px;
line-height: 10px;
height: 22px;
margin-top: -4px;
width: 10px;
z-index: 100;
min-width: 4px;
position: relative;
}
.VS-search .search_input.is_editing input {
color: #202020;
}
/* ================ */
/* = Autocomplete = */
/* ================ */
.VS-interface.ui-autocomplete {
position: absolute;
border: 1px solid #C0C0C0;
border-top: 1px solid #D9D9D9;
background-color: #F6F6F6;
cursor: pointer;
z-index: 10000;
padding: 0;
margin: 0;
width: auto;
min-width: 80px;
max-width: 220px;
max-height: 240px;
overflow-y: auto;
overflow-x: hidden;
font-size: 13px;
top: 5px;
opacity: 0.97;
box-shadow: 3px 4px 5px -2px rgba(0, 0, 0, 0.5); -webkit-box-shadow: 3px 4px 5px -2px rgba(0, 0, 0, 0.5); -moz-box-shadow: 3px 4px 5px -2px rgba(0, 0, 0, 0.5);
}
.VS-interface.ui-autocomplete .ui-autocomplete-category {
text-transform: capitalize;
font-size: 11px;
padding: 4px 4px 4px;
border-top: 1px solid #A2A2A2;
border-bottom: 1px solid #A2A2A2;
background-color: #B7B7B7;
text-shadow: 0 -1px 0 #999;
font-weight: bold;
color: white;
cursor: default;
}
.VS-interface.ui-autocomplete .ui-menu-item {
float: none;
}
.VS-interface.ui-autocomplete .ui-menu-item a {
color: #000;
outline: none;
display: block;
padding: 3px 4px 5px;
border-radius: none;
line-height: 1;
background-color: #F8F8F8;
background-image: -moz-linear-gradient(top, #F8F8F8, #F3F3F3); /* FF3.6 */
background-image: -webkit-gradient(linear, left top, left bottom, from(#F8F8F8), to(#F3F3F3)); /* Saf4+, Chrome */
background-image: linear-gradient(top, #F8F8F8, #F3F3F3);
border-top: 1px solid #FAFAFA;
border-bottom: 1px solid #f0f0f0;
}
.VS-interface.ui-autocomplete .ui-menu-item a:active {
outline: none;
}
.VS-interface.ui-autocomplete .ui-menu-item .ui-state-hover {
background-color: #6483F7;
background-image: -moz-linear-gradient(top, #648bF5, #2465f3); /* FF3.6 */
background-image: -webkit-gradient(linear, left top, left bottom, from(#648bF5), to(#2465f3)); /* Saf4+, Chrome */
background-image: linear-gradient(top, #648bF5, #2465f3);
border-top: 1px solid #5b83ec;
border-bottom: 1px solid #1459e9;
border-left: none;
border-right: none;
color: white;
margin: 0;
}
.VS-interface.ui-autocomplete .ui-corner-all {
border-radius: 0;
}
.VS-interface.ui-autocomplete li {
list-style: none;
width: auto;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,453 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
<meta http-equiv="X-UA-Compatible" content="chrome=1">
<title>DocumentCloud's VisualSearch.js</title>
<style>
body {
font-size: 16px;
line-height: 24px;
background: #FEF3CA;
color: #022;
height: 100%;
font-family: "Palatino Linotype", "Book Antiqua", Palatino, FreeSerif, serif;
}
div.container {
width: 720px;
margin: 50px 0 50px 50px;
}
p, li {
margin: 16px 0 16px 0;
width: 550px;
}
p.break {
margin-top: 35px;
}
ol {
padding-left: 24px;
}
ol li {
font-weight: bold;
margin-left: 0;
}
a, a:visited {
padding: 0 2px;
text-decoration: none;
background: #f0c095;
color: #252519;
}
a:active, a:hover {
color: #FFF;
background: #C25D00;
}
h1, h2, h3, h4, h5, h6 {
margin-top: 40px;
}
b.header {
font-size: 18px;
}
span.alias {
font-size: 14px;
font-style: italic;
margin-left: 20px;
}
table {
margin: 16px 0; padding: 0;
}
tr, td, th {
margin: 0; padding: 0;
text-align: left;
}
th {
padding: 24px 0 0;
}
tr:first-child th {
padding-top: 0;
}
td {
padding: 6px 15px 6px 0;
}
td.definition {
line-height: 18px;
font-size: 14px;
}
table.downloads td {
padding-left: 18px;
}
.demo-hint {
font-size: 13px;
margin: 0 0 12px 12px;
font-weight: normal;
}
#VS code, #VS pre, #VS tt {
font-family: Monaco, Consolas, "Lucida Console", monospace;
font-size: 12px;
line-height: 18px;
color: #444;
background: none;
}
#VS code {
margin-left: 8px;
padding: 0 0 0 12px;
font-weight: normal;
}
#VS pre {
font-size: 12px;
padding: 2px 0 2px 0;
border-left: 6px solid #829C37;
margin: 12px 0;
}
#search_query {
margin: 18px 0;
opacity: 0;
}
#search_query .raquo {
font-size: 18px;
line-height: 12px;
font-weight: bold;
margin-right: 4px;
}
#search_query2 {
margin: 18px 0;
opacity: 0;
}
#search_query2 .raquo {
font-size: 18px;
line-height: 12px;
font-weight: bold;
margin-right: 4px;
}
</style>
<link rel="stylesheet" href="lib/css/reset.css" type="text/css" media="screen" charset="utf-8">
<link rel="stylesheet" href="lib/css/icons.css" type="text/css" media="screen" charset="utf-8">
<link rel="stylesheet" href="lib/css/workspace.css" type="text/css" media="screen" charset="utf-8">
<script src="vendor/jquery-1.6.1.js" type="text/javascript" charset="utf-8"></script>
<!-- <script src="vendor/backported/jquery-1.4.4.js" type="text/javascript" charset="utf-8"></script> -->
<script src="vendor/jquery.ui.core.js" type="text/javascript" charset="utf-8"></script>
<script src="vendor/jquery.ui.widget.js" type="text/javascript" charset="utf-8"></script>
<script src="vendor/jquery.ui.position.js" type="text/javascript" charset="utf-8"></script>
<script src="vendor/jquery.ui.autocomplete.js" type="text/javascript" charset="utf-8"></script>
<script src="vendor/underscore-1.1.5.js" type="text/javascript" charset="utf-8"></script>
<script src="vendor/backbone-0.5.0.js" type="text/javascript" charset="utf-8"></script>
<script src="lib/js/visualsearch.js" type="text/javascript" charset="utf-8"></script>
<script src="lib/js/views/search_box.js" type="text/javascript" charset="utf-8"></script>
<script src="lib/js/views/search_facet.js" type="text/javascript" charset="utf-8"></script>
<script src="lib/js/views/search_input.js" type="text/javascript" charset="utf-8"></script>
<script src="lib/js/models/search_facets.js" type="text/javascript" charset="utf-8"></script>
<script src="lib/js/models/search_query.js" type="text/javascript" charset="utf-8"></script>
<script src="lib/js/utils/backbone_extensions.js" type="text/javascript" charset="utf-8"></script>
<script src="lib/js/utils/hotkeys.js" type="text/javascript" charset="utf-8"></script>
<script src="lib/js/utils/jquery_extensions.js" type="text/javascript" charset="utf-8"></script>
<script src="lib/js/utils/search_parser.js" type="text/javascript" charset="utf-8"></script>
<script src="lib/js/utils/inflector.js" type="text/javascript" charset="utf-8"></script>
<script src="lib/js/templates/templates.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<div class="container" id="VS">
<h1><a href="index.html">VisualSearch.js</a></h1>
<p>
<a href="http://github.com/documentcloud/visualsearch">VisualSearch.js</a>
enhances ordinary search boxes with the ability to autocomplete
faceted search queries. Specify the facets for completion, along with the
completable values for any facet. You can retrieve the search query as a
structured object, so you don't have to parse the query string yourself.
</p>
<p>
<a href="docs/visualsearch.html">The complete annotated source code</a>
is also available.
</p>
<p>
The project is
<a href="http://github.com/documentcloud/visualsearch/">hosted on GitHub</a>.
You can report bugs and discuss features on the
<a href="http://github.com/documentcloud/visualsearch/issues">issues page</a>,
on Freenode in the <tt>#documentcloud</tt> channel,
or send tweets to <a href="http://twitter.com/documentcloud">@documentcloud</a>.
</p>
<p>
<i>VisualSearch.js is an open-source component of <a href="http://documentcloud.org/">DocumentCloud</a>.</i>
</p>
<h2 id="demo">Demo <span class="demo-hint"><i>Try searching for: <b>account</b>, <b>filter</b>, <b>access</b>, <b>title</b>, <b>city</b>, <b>state</b>, or <b>country</b>.</i></span></h2>
<div id="search_box_container"></div>
<div id="search_query">&nbsp;</div>
<script type="text/javascript" charset="utf-8">
$(document).ready(function() {
window.visualSearch = VS.init({
container : $('#search_box_container'),
query : 'country: "South Africa" account: 5-samuel title: "Pentagon Papers"',
// query : '',
unquotable : [
'text',
'account',
'filter',
'access'
],
callbacks : {
search : function(query, searchCollection) {
var $query = $('#search_query');
$query.stop().animate({opacity : 1}, {duration: 300, queue: false});
$query.html('<span class="raquo">&raquo;</span> You searched for: <b>' + searchCollection.serialize() + '</b>');
clearTimeout(window.queryHideDelay);
window.queryHideDelay = setTimeout(function() {
$query.animate({
opacity : 0
}, {
duration: 1000,
queue: false
});
}, 2000);
},
valueMatches : function(category, searchTerm, callback) {
switch (category) {
case 'account':
callback([
{ value: '1-amanda', label: 'Amanda' },
{ value: '2-aron', label: 'Aron' },
{ value: '3-eric', label: 'Eric' },
{ value: '4-jeremy', label: 'Jeremy' },
{ value: '5-samuel', label: 'Samuel' },
{ value: '6-scott', label: 'Scott' }
]);
break;
case 'filter':
callback(['published', 'unpublished', 'draft']);
break;
case 'access':
callback(['public', 'private', 'protected']);
break;
case 'title':
callback([
'Pentagon Papers',
'CoffeeScript Manual',
'Laboratory for Object Oriented Thinking',
'A Repository Grows in Brooklyn'
]);
break;
case 'city':
callback([
'Cleveland',
'New York City',
'Brooklyn',
'Manhattan',
'Queens',
'The Bronx',
'Staten Island',
'San Francisco',
'Los Angeles',
'Seattle',
'London',
'Portland',
'Chicago',
'Boston'
])
break;
case 'state':
callback([
"Alabama", "Alaska", "Arizona", "Arkansas", "California",
"Colorado", "Connecticut", "Delaware", "District of Columbia", "Florida",
"Georgia", "Guam", "Hawaii", "Idaho", "Illinois",
"Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana",
"Maine", "Maryland", "Massachusetts", "Michigan", "Minnesota",
"Mississippi", "Missouri", "Montana", "Nebraska", "Nevada",
"New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina",
"North Dakota", "Ohio", "Oklahoma", "Oregon", "Pennsylvania",
"Puerto Rico", "Rhode Island", "South Carolina", "South Dakota", "Tennessee",
"Texas", "Utah", "Vermont", "Virginia", "Virgin Islands",
"Washington", "West Virginia", "Wisconsin", "Wyoming"
]);
break
case 'country':
callback([
"China", "India", "United States", "Indonesia", "Brazil",
"Pakistan", "Bangladesh", "Nigeria", "Russia", "Japan",
"Mexico", "Philippines", "Vietnam", "Ethiopia", "Egypt",
"Germany", "Turkey", "Iran", "Thailand", "D. R. of Congo",
"France", "United Kingdom", "Italy", "Myanmar", "South Africa",
"South Korea", "Colombia", "Ukraine", "Spain", "Tanzania",
"Sudan", "Kenya", "Argentina", "Poland", "Algeria",
"Canada", "Uganda", "Morocco", "Iraq", "Nepal",
"Peru", "Afghanistan", "Venezuela", "Malaysia", "Uzbekistan",
"Saudi Arabia", "Ghana", "Yemen", "North Korea", "Mozambique",
"Taiwan", "Syria", "Ivory Coast", "Australia", "Romania",
"Sri Lanka", "Madagascar", "Cameroon", "Angola", "Chile",
"Netherlands", "Burkina Faso", "Niger", "Kazakhstan", "Malawi",
"Cambodia", "Guatemala", "Ecuador", "Mali", "Zambia",
"Senegal", "Zimbabwe", "Chad", "Cuba", "Greece",
"Portugal", "Belgium", "Czech Republic", "Tunisia", "Guinea",
"Rwanda", "Dominican Republic", "Haiti", "Bolivia", "Hungary",
"Belarus", "Somalia", "Sweden", "Benin", "Azerbaijan",
"Burundi", "Austria", "Honduras", "Switzerland", "Bulgaria",
"Serbia", "Israel", "Tajikistan", "Hong Kong", "Papua New Guinea",
"Togo", "Libya", "Jordan", "Paraguay", "Laos",
"El Salvador", "Sierra Leone", "Nicaragua", "Kyrgyzstan", "Denmark",
"Slovakia", "Finland", "Eritrea", "Turkmenistan"
], {preserveOrder: true});
break;
}
},
facetMatches : function(callback) {
callback([
'account', 'filter', 'access', 'title',
{ label: 'city', category: 'location' },
{ label: 'address', category: 'location' },
{ label: 'country', category: 'location' },
{ label: 'state', category: 'location' },
]);
}
}
});
});
</script>
<div id="search_box_container2"></div>
<div id="search_query2">&nbsp;</div>
<script type="text/javascript" charset="utf-8">
$(document).ready(function() {
var visualSearch = VS.init({
container : $('#search_box_container2'),
query : '',
// query : '',
unquotable : [
'text',
'account',
'filter',
'access'
],
callbacks : {
search : function(query, searchCollection) {
console.log(["query", searchCollection.facets(), query]);
var $query = $('#search_query2');
$query.stop().animate({opacity : 1}, {duration: 300, queue: false});
$query.html('<span class="raquo">&raquo;</span> You searched for: <b>' + searchCollection.serialize() + '</b>');
clearTimeout(window.queryHideDelay2);
window.queryHideDelay2 = setTimeout(function() {
$query.animate({
opacity : 0
}, {
duration: 1000,
queue: false
});
}, 2000);
},
valueMatches : function(category, searchTerm, callback) {
switch (category) {
case 'account':
callback([
{ value: '1-amanda', label: 'Amanda' },
{ value: '2-aron', label: 'Aron' },
{ value: '3-eric', label: 'Eric' },
{ value: '4-jeremy', label: 'Jeremy' },
{ value: '5-samuel', label: 'Samuel' },
{ value: '6-scott', label: 'Scott' }
]);
break;
case 'filter':
callback(['published', 'unpublished', 'draft']);
break;
case 'access':
callback(['public', 'private', 'protected']);
break;
case 'title':
callback([
'Pentagon Papers',
'CoffeeScript Manual',
'Laboratory for Object Oriented Thinking',
'A Repository Grows in Brooklyn'
]);
break;
case 'city':
callback([
'Cleveland',
'New York City',
'Brooklyn',
'Manhattan',
'Queens',
'The Bronx',
'Staten Island',
'San Francisco',
'Los Angeles',
'Seattle',
'London',
'Portland',
'Chicago',
'Boston'
])
break;
case 'state':
callback([
"Alabama", "Alaska", "Arizona", "Arkansas", "California",
"Colorado", "Connecticut", "Delaware", "District of Columbia", "Florida",
"Georgia", "Guam", "Hawaii", "Idaho", "Illinois",
"Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana",
"Maine", "Maryland", "Massachusetts", "Michigan", "Minnesota",
"Mississippi", "Missouri", "Montana", "Nebraska", "Nevada",
"New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina",
"North Dakota", "Ohio", "Oklahoma", "Oregon", "Pennsylvania",
"Puerto Rico", "Rhode Island", "South Carolina", "South Dakota", "Tennessee",
"Texas", "Utah", "Vermont", "Virginia", "Virgin Islands",
"Washington", "West Virginia", "Wisconsin", "Wyoming"
]);
break
case 'country':
callback([
"China", "India", "United States", "Indonesia", "Brazil",
"Pakistan", "Bangladesh", "Nigeria", "Russia", "Japan",
"Mexico", "Philippines", "Vietnam", "Ethiopia", "Egypt",
"Germany", "Turkey", "Iran", "Thailand", "D. R. of Congo",
"France", "United Kingdom", "Italy", "Myanmar", "South Africa",
"South Korea", "Colombia", "Ukraine", "Spain", "Tanzania",
"Sudan", "Kenya", "Argentina", "Poland", "Algeria",
"Canada", "Uganda", "Morocco", "Iraq", "Nepal",
"Peru", "Afghanistan", "Venezuela", "Malaysia", "Uzbekistan",
"Saudi Arabia", "Ghana", "Yemen", "North Korea", "Mozambique",
"Taiwan", "Syria", "Ivory Coast", "Australia", "Romania",
"Sri Lanka", "Madagascar", "Cameroon", "Angola", "Chile",
"Netherlands", "Burkina Faso", "Niger", "Kazakhstan", "Malawi",
"Cambodia", "Guatemala", "Ecuador", "Mali", "Zambia",
"Senegal", "Zimbabwe", "Chad", "Cuba", "Greece",
"Portugal", "Belgium", "Czech Republic", "Tunisia", "Guinea",
"Rwanda", "Dominican Republic", "Haiti", "Bolivia", "Hungary",
"Belarus", "Somalia", "Sweden", "Benin", "Azerbaijan",
"Burundi", "Austria", "Honduras", "Switzerland", "Bulgaria",
"Serbia", "Israel", "Tajikistan", "Hong Kong", "Papua New Guinea",
"Togo", "Libya", "Jordan", "Paraguay", "Laos",
"El Salvador", "Sierra Leone", "Nicaragua", "Kyrgyzstan", "Denmark",
"Slovakia", "Finland", "Eritrea", "Turkmenistan"
]);
break;
}
},
facetMatches : function(callback) {
callback([
'account', 'filter', 'access', 'title',
{ label: 'city', category: 'location' },
{ label: 'address', category: 'location' },
{ label: 'country', category: 'location' },
{ label: 'state', category: 'location' },
], {
preserveOrder: true
});
}
}
});
});
</script>
</div>
</body>
</html>

View File

@ -1,537 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
<meta http-equiv="X-UA-Compatible" content="chrome=1">
<title>DocumentCloud's VisualSearch.js</title>
<style>
body {
font-size: 16px;
line-height: 24px;
background: #FEF3CA;
height: 100%;
color: #022;
font-family: Arial;
font-family: "Palatino Linotype", "Book Antiqua", Palatino, FreeSerif, serif;
}
div.container {
width: 720px;
margin: 50px 0 50px 50px;
}
p, li {
margin: 16px 0 16px 0;
width: 550px;
}
p.break {
margin-top: 35px;
}
ol {
padding-left: 24px;
}
ol li {
font-weight: bold;
margin-left: 0;
}
a, a:visited {
padding: 0 2px;
text-decoration: none;
background: #f0c095;
color: #252519;
}
a:active, a:hover {
color: #000;
background: #e0a070;
}
h1, h2, h3, h4, h5, h6 {
margin-top: 40px;
}
b.header {
font-size: 18px;
}
span.alias {
font-size: 14px;
font-style: italic;
margin-left: 20px;
}
table {
margin: 16px 0; padding: 0;
}
tr, td, th {
margin: 0; padding: 0;
text-align: left;
}
th {
padding: 24px 0 0;
}
tr:first-child th {
padding-top: 0;
}
td {
padding: 6px 15px 6px 0;
}
td.definition {
line-height: 18px;
font-size: 14px;
}
table.downloads td {
padding-left: 18px;
}
.demo-hint {
font-size: 13px;
margin: 0 0 12px 12px;
font-weight: normal;
}
#VS code, #VS pre, #VS tt {
font-family: Monaco, Consolas, "Lucida Console", monospace;
font-size: 12px;
line-height: 18px;
color: #444;
background: none;
}
#VS code {
margin-left: 8px;
padding: 0 0 0 12px;
font-weight: normal;
}
#VS pre {
font-size: 12px;
padding: 2px 0 2px 0;
border-left: 6px solid #829C37;
margin: 12px 0;
}
#search_query {
margin: 18px 0 -24px;
opacity: 0;
}
#search_query .raquo {
font-size: 18px;
line-height: 12px;
font-weight: bold;
margin-right: 4px;
}
.attribution {
margin: -12px 0 24px;
font-size: 14px;
}
</style>
<link rel="stylesheet" href="build-min/visualsearch-datauri.css" type="text/css" media="screen" charset="utf-8">
<link rel="stylesheet" href="docs/assets/github.css" type="text/css" media="screen" charset="utf-8">
<script src="build-min/dependencies.js" type="text/javascript" charset="utf-8"></script>
<script src="build-min/visualsearch.js" type="text/javascript" charset="utf-8"></script>
<script src="docs/assets/highlight.js" type="text/javascript" charset="utf-8"></script>
<script src="docs/assets/javascript.js" type="text/javascript" charset="utf-8"></script>
<script src="docs/assets/xml.js" type="text/javascript" charset="utf-8"></script>
<script>
hljs.initHighlightingOnLoad();
</script>
</head>
<body>
<div class="container" id="VS">
<h1>VisualSearch.js</h1>
<p class="attribution">
<i>
Created by <a href="http://github.com/samuelclay">Samuel Clay</a>,
<a href="http://twitter.com/samuelclay">@samuelclay</a>.
</i>
</p>
<p>
<a href="http://github.com/documentcloud/visualsearch">VisualSearch.js</a>
enhances ordinary search boxes with the ability to autocomplete
faceted search queries. Specify the facets for completion, along with the
completable values for any facet. You can retrieve the search query as a
structured object, so you don't have to parse the query string yourself.
</p>
<p>
<a href="https://www.documentcloud.org/public/#search/group%3A%20dcloud%20chair%3A%20%22Christopher%20Dodd%22%20TARP">Here's an example of a search on DocumentCloud.org that uses facets.</a>
</p>
<p>
The project is
<a href="http://github.com/documentcloud/visualsearch/">hosted on GitHub</a>.
You can report bugs and discuss features on the
<a href="http://github.com/documentcloud/visualsearch/issues">issues page</a>,
on Freenode in the <tt>#documentcloud</tt> channel,
or send tweets to <a href="http://twitter.com/documentcloud">@documentcloud</a>.
</p>
<p>
<i>VisualSearch.js is an open-source component of <a href="http://documentcloud.org/">DocumentCloud</a>.
<br />
<a href="docs/visualsearch.html">The complete annotated source code</a>
is also available.</i>
</p>
<h2>Table of Contents</h2>
<a href="#demo">Demo</a> | <a href="#downloads">Downloads</a> | <a href="#usage">Usage</a> | <a href="#changelog">Change Log</a>
<h2 id="demo">Demo <span class="demo-hint"><i>Try searching for: <b>account</b>, <b>filter</b>, <b>access</b>, <b>title</b>, <b>city</b>, <b>state</b>, or <b>country</b>.</i></span></h2>
<div id="search_box_container"></div>
<div id="search_query">&nbsp;</div>
<script type="text/javascript" charset="utf-8">
$(document).ready(function() {
var visualSearch = VS.init({
container : $('#search_box_container'),
query : 'country: "United States" state: "New York" account: 5-samuel title: "Pentagon Papers"',
unquotable : [
'text',
'account',
'filter',
'access'
],
callbacks : {
search : function(query, searchCollection) {
var $query = $('#search_query');
var count = searchCollection.size();
$query.stop().animate({opacity : 1}, {duration: 300, queue: false});
$query.html('<span class="raquo">&raquo;</span> You searched for: ' +
'<b>' + (query || '<i>nothing</i>') + '</b>. ' +
'(' + count + ' facet' + (count==1 ? '' : 's') + ')');
clearTimeout(window.queryHideDelay);
window.queryHideDelay = setTimeout(function() {
$query.animate({
opacity : 0
}, {
duration: 1000,
queue: false
});
}, 2000);
},
facetMatches : function(callback) {
callback([
'account', 'filter', 'access', 'title',
{ label: 'city', category: 'location' },
{ label: 'country', category: 'location' },
{ label: 'state', category: 'location' },
]);
},
valueMatches : function(facet, searchTerm, callback) {
switch (facet) {
case 'account':
callback([
{ value: '1-amanda', label: 'Amanda' },
{ value: '2-aron', label: 'Aron' },
{ value: '3-eric', label: 'Eric' },
{ value: '4-jeremy', label: 'Jeremy' },
{ value: '5-samuel', label: 'Samuel' },
{ value: '6-scott', label: 'Scott' }
]);
break;
case 'filter':
callback(['published', 'unpublished', 'draft']);
break;
case 'access':
callback(['public', 'private', 'protected']);
break;
case 'title':
callback([
'Pentagon Papers',
'CoffeeScript Manual',
'Laboratory for Object Oriented Thinking',
'A Repository Grows in Brooklyn'
]);
break;
case 'city':
callback([
'Cleveland',
'New York City',
'Brooklyn',
'Manhattan',
'Queens',
'The Bronx',
'Staten Island',
'San Francisco',
'Los Angeles',
'Seattle',
'London',
'Portland',
'Chicago',
'Boston'
]);
break;
case 'state':
callback([
"Alabama", "Alaska", "Arizona", "Arkansas", "California",
"Colorado", "Connecticut", "Delaware", "District of Columbia", "Florida",
"Georgia", "Guam", "Hawaii", "Idaho", "Illinois",
"Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana",
"Maine", "Maryland", "Massachusetts", "Michigan", "Minnesota",
"Mississippi", "Missouri", "Montana", "Nebraska", "Nevada",
"New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina",
"North Dakota", "Ohio", "Oklahoma", "Oregon", "Pennsylvania",
"Puerto Rico", "Rhode Island", "South Carolina", "South Dakota", "Tennessee",
"Texas", "Utah", "Vermont", "Virginia", "Virgin Islands",
"Washington", "West Virginia", "Wisconsin", "Wyoming"
]);
break;
case 'country':
callback([
"China", "India", "United States", "Indonesia", "Brazil",
"Pakistan", "Bangladesh", "Nigeria", "Russia", "Japan",
"Mexico", "Philippines", "Vietnam", "Ethiopia", "Egypt",
"Germany", "Turkey", "Iran", "Thailand", "D. R. of Congo",
"France", "United Kingdom", "Italy", "Myanmar", "South Africa",
"South Korea", "Colombia", "Ukraine", "Spain", "Tanzania",
"Sudan", "Kenya", "Argentina", "Poland", "Algeria",
"Canada", "Uganda", "Morocco", "Iraq", "Nepal",
"Peru", "Afghanistan", "Venezuela", "Malaysia", "Uzbekistan",
"Saudi Arabia", "Ghana", "Yemen", "North Korea", "Mozambique",
"Taiwan", "Syria", "Ivory Coast", "Australia", "Romania",
"Sri Lanka", "Madagascar", "Cameroon", "Angola", "Chile",
"Netherlands", "Burkina Faso", "Niger", "Kazakhstan", "Malawi",
"Cambodia", "Guatemala", "Ecuador", "Mali", "Zambia",
"Senegal", "Zimbabwe", "Chad", "Cuba", "Greece",
"Portugal", "Belgium", "Czech Republic", "Tunisia", "Guinea",
"Rwanda", "Dominican Republic", "Haiti", "Bolivia", "Hungary",
"Belarus", "Somalia", "Sweden", "Benin", "Azerbaijan",
"Burundi", "Austria", "Honduras", "Switzerland", "Bulgaria",
"Serbia", "Israel", "Tajikistan", "Hong Kong", "Papua New Guinea",
"Togo", "Libya", "Jordan", "Paraguay", "Laos",
"El Salvador", "Sierra Leone", "Nicaragua", "Kyrgyzstan", "Denmark",
"Slovakia", "Finland", "Eritrea", "Turkmenistan"
], {preserveOrder: true});
break;
}
}
}
});
});
</script>
<h2 id="downloads">Downloads <i style="padding-left: 12px; font-size:12px;">(Right-click, and use "Save As")</i></h2>
<table class="downloads">
<tr>
<th colspan="2">0. Everything (<tt>visualsearch.zip</tt>)</th>
</tr>
<tr>
<td><a href="https://github.com/documentcloud/visualsearch/zipball/master">Download everything</a></td>
</tr>
<tr>
<th colspan="2">1. VisualSearch JavaScript (<tt>visualsearch.js</tt>)</th>
</tr>
<tr>
<td><a href="build-min/visualsearch.js">Production Version (0.2.2)</a></td>
<td><i>8kb, Minified and Gzipped</i></td>
</tr>
<tr>
<td><a href="build/visualsearch.js">Development Version (0.2.2)</a></td>
<td><i>45kb, Uncompressed with Comments</i></td>
</tr>
<tr>
<th colspan="2">2. VisualSearch Stylesheets (<tt>visualsearch.css</tt>)</th>
</tr>
<tr>
<td colspan="2"><i>You should include both the datauri and image urls versions. <a href="#css">See how to include both</a></i></td>
</tr>
<tr>
<td><a href="build-min/visualsearch-datauri.css">Production Version - datauri</a></td>
<td><i>4kb, Minified and Gzipped</i></td>
</tr>
<tr>
<td><a href="build-min/visualsearch.css">Production Version - image urls</a></td>
<td><i>4kb, Minified and Gzipped</i></td>
</tr>
<tr>
<td><a href="build/visualsearch.css">Development Version</a></td>
<td><i>8kb, Uncompressed with Comments</i></td>
</tr>
<tr>
<th colspan="2">3. VisualSearch Images</th>
</tr>
<tr>
<td><img src="lib/images/embed/icons/search_glyph.png"> <a href="lib/images/embed/icons/search_glyph.png">Search Glyph</a></td>
<td><i>4kb, embedded in visualsearch-datauri.css</i></td>
</tr>
<tr>
<td><img src="lib/images/embed/icons/cancel_search.png"> <a href="lib/images/embed/icons/cancel_search.png">Cancel Button</a></td>
<td><i>4kb, embedded in visualsearch-datauri.css</i></td>
</tr>
<tr>
<th colspan="2">4. VisualSearch Dependencies (<tt>jQuery 1.4+, jQuery UI, Underscore.js, Backbone.js</tt>)</th>
</tr>
<tr>
<td colspan="2"><i>You should only include the dependencies you don't already have.</i></td>
</tr>
<tr>
<td><a href="build-min/dependencies.js">Production Version - All</a></td>
<td><i>49kb, Minified and Gzipped</i></td>
</tr>
<tr>
<td><a href="build/dependencies.js">Development Version - All</a></td>
<td><i>340kb, Uncompressed with Comments</i></td>
</tr>
<tr>
<td><a href="vendor/jquery-1.6.1.js">jQuery 1.6.1</a></td>
<td><i>238kb, Uncompressed with Comments</i></td>
</tr>
<tr>
<td>jQuery UI 1.8.13: <br /><a href="vendor/jquery.ui.core.js">Core</a> <a href="vendor/jquery.ui.position.js">Position</a> <a href="vendor/jquery.ui.widget.js">Widget</a> <a href="vendor/jquery.ui.autocomplete.js">Autocomplete</a></td>
<td><i>48kb, Uncompressed with Comments</i></td>
</tr>
<tr>
<td><a href="vendor/underscore-1.1.5.js">Underscore 1.1.5</a></td>
<td><i>29kb, Uncompressed with Comments</i></td>
</tr>
<tr>
<td><a href="vendor/backbone-0.5.0.js">Backbone 0.5.0</a></td>
<td><i>41kb, Uncompressed with Comments</i></td>
</tr>
</table>
<h2 id="usage">Usage</h2>
<p>To use VisualSearch.js on your site, follow these instructions on installation, configuration, and customization.</p>
<ol>
<li class="xml" id="css">Insert the JavaScript and CSS into your page:<br />
<pre><code>&lt;script src="visualsearch.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;!--[if (!IE)|(gte IE 8)]&gt;&lt;!--&gt;
&lt;link href="visualsearch-datauri.css" media="screen" rel="stylesheet" type="text/css" /&gt;
&lt;!--&lt;![endif]--&gt;
&lt;!--[if lte IE 7]&gt;&lt;!--&gt;
&lt;link href="visualsearch.css" media="screen" rel="stylesheet" type="text/css" /&gt;
&lt;!--&lt;![endif]--&gt;</code></pre>
</li>
<li>Initialize the Visual Search box:<br />
<pre class="javascript"><code>&lt;div class="visual_search"&gt;&lt;/div&gt;
&lt;script type="text/javascript" charset="utf-8"&gt;
$(document).ready(function() {
var visualSearch = VS.init({
container : $('.visual_search'),
query : '',
callbacks : {
search : function(query, searchCollection) {},
facetMatches : function(callback) {},
valueMatches : function(facet, searchTerm, callback) {}
}
});
});
&lt;/script&gt;</code></pre>
</li>
<li class="javascript">Customize the autocompleted facets and values:
<pre><code>callbacks : {
...
// These are the facets that will be autocompleted in an empty input.
facetMatches : function(callback) {
callback([
'account', 'filter', 'access', 'title',
{ label: 'city', category: 'location' },
{ label: 'address', category: 'location' },
{ label: 'country', category: 'location' },
{ label: 'state', category: 'location' },
]);
}
...
// These are the values that match specific categories, autocompleted
// in a category's input field. searchTerm can be used to filter the
// list on the server-side, prior to providing a list to the widget.
valueMatches : function(facet, searchTerm, callback) {
switch (facet) {
case 'account':
callback([
{ value: '1-amanda', label: 'Amanda' },
{ value: '2-aron', label: 'Aron' },
{ value: '3-eric', label: 'Eric' },
{ value: '4-jeremy', label: 'Jeremy' },
{ value: '5-samuel', label: 'Samuel' },
{ value: '6-scott', label: 'Scott' }
]);
break;
case 'filter':
callback(['published', 'unpublished', 'draft']);
break;
case 'access':
callback(['public', 'private', 'protected']);
break;
case 'title':
callback([
'Pentagon Papers',
'CoffeeScript Manual',
'Laboratory for Object Oriented Thinking',
'A Repository Grows in Brooklyn'
]);
break;
}
}
...
}</code></pre>
</li>
<li class="javascript">Inspect the Visual Search box
<pre><code>// Returns the unstructured search query
visualSearch.searchBox.value()
// "country: "South Africa" account: 5-samuel title: "Pentagon Papers""
// Returns an array of Facet model instances
visualSearch.searchQuery.facets()
// [FacetModel&lt;country:"South Africa">,
// FacetModel&lt;account:5-samuel>,
// FacetModel&lt;title:"Pentagon Papers">]
// Set the search query with raw text
visualSearch.searchBox.value("Country: US State: \"New York\" Key: Value")
</code></pre>
</li>
</ol>
<h2 id="changelog">Change Log</h2>
<p>
<b class="header">0.2.2</b> <i>March 10th, 2012</i><br />
If you do not want to automatically filter the value matches, you can pass an
options hash with <tt>preserveMatches: true</tt> as the second argument to the callback.
<a href="https://github.com/documentcloud/visualsearch/pull/44">See pull request #44</a> for details.
</p>
<p>
<b class="header">0.2.1</b> <i>November 14th, 2011</i><br />
The autocompleted facets and values that are provided by your callbacks <tt>facetMatches</tt>
and <tt>valueMatches</tt> can now preserve the order of items you give them. Simply pass an
options hash with <tt>preserveOrder: true</tt> as the second argument to the callback. See
<a href="demo.html">the demo page</a> for an example.
</p>
<p>
<b class="header">0.2.0</b> <i>August 10th, 2011</i><br />
Multiple instances of VisualSearch on a single page. <tt>VS.init</tt> now returns
a reference to the instance. The <tt>search</tt> callback now contains both the
serialized search query and a reference to the search query collection (as a
<a href="http://documentcloud.github.com/backbone/#Collection">Backbone.Collection</a>),
which can be used to manipulate each facet directly. See
<a href="docs/search_query.html">the source code for search_query.js</a> for available
methods on the collection.
</p>
<p>
<b class="header">0.1.0</b> <i>June 23rd, 2011</i><br />
Initial release of VisualSearch.js.
</p>
<p>
<a href="http://documentcloud.org/" title="A DocumentCloud Project" style="background:none;">
<img src="http://jashkenas.s3.amazonaws.com/images/a_documentcloud_project.png" alt="A DocumentCloud Project" />
</a>
</p>
</div>
</body>
</html>

View File

@ -1160,140 +1160,164 @@
white-space: nowrap;
}
.openerp .oe_searchview {
cursor: text;
position: relative;
float: right;
}
.openerp .oe_searchview .VS-search .VS-search-box {
min-height: 0;
padding: 0;
width: 480px;
padding-right: 20px;
width: 410px;
border: 1px solid #ababab;
-moz-border-radius: 13px;
-webkit-border-radius: 13px;
border-radius: 13px;
background: white;
-moz-border-radius: 1em;
-webkit-border-radius: 1em;
border-radius: 1em;
-moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2) inset;
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2) inset;
-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2) inset;
}
.openerp .oe_searchview .VS-search .VS-icon {
top: 6px;
.openerp .oe_searchview.oe_focused {
border-color: #a6a6fe;
-moz-box-shadow: 0 1px 2px #a6a6fe inset;
-webkit-box-shadow: 0 1px 2px #a6a6fe inset;
-box-shadow: 0 1px 2px #a6a6fe inset;
}
.openerp .oe_searchview .VS-search .VS-icon-search {
left: 2px;
top: 3px;
height: 18px;
width: 18px;
.openerp .oe_searchview .oe_searchview_clear {
cursor: pointer;
position: absolute;
top: 0;
right: 22px;
width: 15px;
height: 100%;
background: url(../img/attachments-close.png) center center no-repeat;
}
.openerp .oe_searchview .VS-search .VS-search-inner {
margin: 0 40px 0 17px;
padding: 1px 0;
font-size: 13px;
.openerp .oe_searchview .oe_searchview_unfold_drawer {
position: absolute;
top: 0;
right: 0;
height: 100%;
line-height: 2.5em;
padding: 0 7px 0 4px;
color: #cccccc;
cursor: pointer;
}
.openerp .oe_searchview .VS-search .VS-search-inner input {
font-size: inherit;
line-height: inherit;
height: auto;
padding: 0;
.openerp .oe_searchview .oe_searchview_unfold_drawer:hover {
color: #999999;
}
.openerp .oe_searchview .VS-search .VS-search-inner .VS-input-width-tester {
font-size: inherit;
padding: 0;
.openerp .oe_searchview .oe_searchview_unfold_drawer:before {
content: "◀";
}
.openerp .oe_searchview .VS-search .VS-search-inner .search_input {
margin-left: 0;
height: 22px;
.openerp .oe_searchview .oe_searchview_facets:before {
color: #cccccc;
font-family: "mnmliconsRegular";
content: "r";
font-size: 150%;
padding: 0 1px 0 3px;
display: inline;
}
.openerp .oe_searchview .VS-search .VS-search-inner .search_input input, .openerp .oe_searchview .VS-search .VS-search-inner .search_input .VS-input-width-tester {
height: inherit;
margin: 0;
.openerp .oe_searchview .oe_searchview_facets * {
vertical-align: top;
display: inline-block;
line-height: 26px;
}
.openerp .oe_searchview .VS-search .VS-search-inner .search_facet {
padding: 0;
margin: 1px 0;
.openerp .oe_searchview .oe_searchview_facets .oe_searchview_input, .openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet {
height: 26px;
}
.openerp .oe_searchview .oe_searchview_facets .oe_searchview_input:focus, .openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet:focus {
outline: none;
}
.openerp .oe_searchview .oe_searchview_facets .oe_searchview_input {
padding: 0 3px;
}
.openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet {
position: relative;
cursor: pointer;
border: 1px solid #afafb6;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
border-radius: 3px;
background: #f0f0fa;
height: 18px;
background: #8786b7;
-webkit-font-smoothing: auto;
padding-left: 1.1em;
margin: 1px 0;
}
.openerp .oe_searchview .VS-search .VS-search-inner .search_facet.is_selected {
.openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet:focus {
border-color: #a6a6fe;
-moz-box-shadow: 0 0 3px 1px #a6a6fe;
-webkit-box-shadow: 0 0 3px 1px #a6a6fe;
-box-shadow: 0 0 3px 1px #a6a6fe;
}
.openerp .oe_searchview .VS-search .VS-search-inner .search_facet.is_selected .category {
margin-left: 0;
.openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet .oe_facet_values {
background: #f0f0fa;
-moz-border-radius: 0 3px 3px 0;
-webkit-border-radius: 0 3px 3px 0;
border-radius: 0 3px 3px 0;
}
.openerp .oe_searchview .VS-search .VS-search-inner .search_facet.is_selected .VS-icon-cancel {
filter: alpha(opacity=100);
opacity: 1;
background-position: center 0;
.openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet .oe_facet_category, .openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet .oe_facet_value {
height: 24px;
padding: 1px 0.1em;
}
.openerp .oe_searchview .VS-search .VS-search-inner .search_facet .category {
padding: 0 4px 0 14px;
.openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet .oe_facet_category {
color: white;
background: #8786b7;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.4);
font-weight: normal;
text-transform: none;
height: 18px;
line-height: 18px;
font-size: inherit;
}
.openerp .oe_searchview .VS-search .VS-search-inner .search_facet .search_facet_input_container {
.openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet .oe_facet_value {
border-left: 1px solid #afafb6;
height: 18px;
line-height: 18px;
padding: 0 4px;
cursor: pointer;
}
.openerp .oe_searchview .VS-search .VS-search-inner .search_facet .search_facet_remove {
left: 1px;
top: 3px;
}
.openerp .oe_searchview .VS-search .VS-icon-cancel {
right: 24px;
}
.openerp .oe_searchview .VS-search .oe_vs_unfold_drawer {
.openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet .oe_facet_remove {
position: absolute;
top: 0;
right: 0;
height: 100%;
line-height: 23px;
padding: 0 7px 0 4px;
color: #4c4c4c;
cursor: pointer;
left: 2px;
color: white;
}
.openerp .oe_searchview .VS-search .oe_vs_unfold_drawer:before {
position: absolute;
top: 9px;
right: 8px;
width: 0;
height: 0;
display: inline-block;
content: "";
vertical-align: top;
border-top: 5px solid #4c4c4c;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
filter: alpha(opacity=50);
opacity: 0.5;
.openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet .oe_facet_remove:hover {
color: #cccccc;
}
.openerp .oe_searchview.oe_searchview_open_drawer .oe_searchview_drawer {
display: block;
}
.openerp .oe_searchview.oe_searchview_open_drawer .oe_searchview_unfold_drawer:before {
content: "▼";
}
.openerp .oe_searchview .oe_searchview_drawer {
position: absolute;
z-index: 1;
margin-top: 3px;
top: 100%;
right: 0;
background-color: white;
width: 480px;
min-width: 100%;
display: none;
border: 1px solid #cccccc;
text-align: left;
padding-bottom: 0.5em;
-moz-border-radius: 1em;
-webkit-border-radius: 1em;
border-radius: 1em;
}
.openerp .oe_searchview .oe_searchview_drawer > div {
border-top: 1px solid #cccccc;
margin: 3px 0;
}
.openerp .oe_searchview .oe_searchview_drawer > div:first-child {
border-top: none;
margin: 0;
}
.openerp .oe_searchview .oe_searchview_drawer h4, .openerp .oe_searchview .oe_searchview_drawer h4 * {
margin: 0;
cursor: pointer;
}
.openerp .oe_searchview .oe_searchview_drawer h4:before {
content: "▸ ";
}
.openerp .oe_searchview .oe_searchview_drawer button, .openerp .oe_searchview .oe_searchview_drawer .button {
border: none;
background: transparent;
padding: 0 2px;
-moz-box-shadow: none;
-webkit-box-shadow: none;
-box-shadow: none;
-moz-border-radius: 0;
-webkit-border-radius: 0;
border-radius: 0;
}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_filters {
display: table;
@ -1317,23 +1341,49 @@
}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_filters li {
list-style: none;
padding: 3px 6px;
height: 14px;
padding: 3px 6px 3px 18px;
line-height: 14px;
color: inherit;
cursor: pointer;
}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_filters li.oe_selected {
background: url(/web/static/src/img/icons/gtk-apply.png) left 2px no-repeat;
}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_filters li:hover {
background-color: #f0f0fa;
}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced h4 {
border-top: 1px solid #cccccc;
margin: 5px 0 3px;
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_custom form {
display: none;
}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_custom form button {
font-size: 1px;
letter-spacing: -1px;
color: transparent;
}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_custom form button:before {
font-family: "mnmliconsRegular";
content: "S";
font-size: 20px;
color: #404040;
}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_custom li {
cursor: pointer;
position: relative;
line-height: 1.2em;
padding: 2px 20px 2px 25px;
}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced h4:before {
content: "▸ ";
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_custom li.oe_searchview_custom_private {
background: url(/web/static/src/img/icons/terp-locked.png) 5px center no-repeat;
}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced div {
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_custom li:hover {
background-color: #f0f0fa;
}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_custom li button {
position: absolute;
top: 0;
right: 5px;
}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced form {
display: none;
}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced button.oe_add_condition:before {
@ -1349,13 +1399,25 @@
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced li {
list-style: none;
margin: 0;
white-space: nowrap;
}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced.oe_opened h4:before {
.openerp .oe_searchview .oe_searchview_drawer .oe_opened h4:before {
content: "▾ ";
}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced.oe_opened div {
.openerp .oe_searchview .oe_searchview_drawer .oe_opened form {
display: block;
}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_custom_delete, .openerp .oe_searchview .oe_searchview_drawer .searchview_extended_delete_prop {
font-size: 1px;
letter-spacing: -1px;
color: transparent;
}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_custom_delete:before, .openerp .oe_searchview .oe_searchview_drawer .searchview_extended_delete_prop:before {
font-family: "mnmliconsRegular";
content: "d";
font-size: 20px;
color: #404040;
}
.openerp .oe_view_nocontent > img {
float: left;
margin: 1.5em;

View File

@ -4,6 +4,7 @@
$section-title-color: #8786b7
$facets-border: #afafb6
$facets-border-selected: #a6a6fe
$hover-background: #f0f0fa
$colour4: #8a89ba
// }}}
// Mixins {{{
@ -74,6 +75,18 @@ $colour4: #8a89ba
-webkit-box-sizing: border-box
box-sizing: border-box
// Transforms the (readable) text of an inline element into an mmlicons icon,
// allows for actual readable text in-code (and in readers?) with iconic looks
@mixin text-to-icon($icon-name, $color: #404040)
font-size: 1px
letter-spacing: -1px
color: transparent
&:before
font-family: "mnmliconsRegular"
content: $icon-name
font-size: 20px
color: $color
// }}}
.openerp.openerp-web-client-container
@ -918,132 +931,145 @@ $colour4: #8a89ba
// }}}
// SearchView xmo {{{
.oe_searchview
cursor: text
position: relative
float: right
.VS-search
.VS-search-box
min-height: 0
padding: 0
width: 480px
border: 1px solid #ababab
@include radius(13px)
@include box-shadow(0 1px 2px rgba(0,0,0,0.2) inset)
//padding: 4px 19px
//font-size: 13px
//background: url('../img/search.png') no-repeat 5px
//background-color: white
.VS-icon
top: 6px
padding-right: 20px
width: 410px
border: 1px solid #ababab
background: white
@include radius(1em)
@include box-shadow(0 1px 2px rgba(0,0,0,0.2) inset)
.VS-icon-search
left: 2px
top: 3px
height: 18px
width: 18px
&.oe_focused
border-color: $facets-border-selected
@include box-shadow(0 1px 2px $facets-border-selected inset)
.VS-search-inner
margin: 0 40px 0 17px
padding: 1px 0
font-size: 13px
.oe_searchview_clear
cursor: pointer
position: absolute
top: 0
right: 22px
width: 15px
height: 100%
background: url(../img/attachments-close.png) center center no-repeat
input
font-size: inherit
line-height: inherit
height: auto
padding: 0
.oe_searchview_unfold_drawer
position: absolute
top: 0
right: 0
height: 100%
line-height: 2.5em
padding: 0 7px 0 4px
color: #ccc
cursor: pointer
&:hover
color: #999
&:before
content: ""
.VS-input-width-tester
font-size: inherit
padding: 0
.oe_searchview_facets
&:before
color: #ccc
font-family: "mnmliconsRegular"
content: "r"
font-size: 150%
padding: 0 1px 0 3px
display: inline
.search_input
margin-left: 0
height: 22px
input, .VS-input-width-tester
height: inherit
margin: 0
*
vertical-align: top
display: inline-block
line-height: 26px
.search_facet
padding: 0
margin: 1px 0
border: 1px solid $facets-border
@include radius(3px)
background: #f0f0fa
height: 18px
.oe_searchview_input, .oe_searchview_facet
height: 26px
&:focus
outline: none
&.is_selected
border-color: $facets-border-selected
@include box-shadow(0 0 3px 1px $facets-border-selected)
.oe_searchview_input
padding: 0 3px
.category
margin-left: 0
.VS-icon-cancel
// don't change the icon on selection
@include opacity(1.0)
background-position: center 0
.category
padding: 0 4px 0 14px
color: white
background: #8786b7
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.4)
font-weight: normal
text-transform: none
height: 18px
line-height: 18px
font-size: inherit
.search_facet_input_container
border-left: 1px solid $facets-border
height: 18px
line-height: 18px
padding: 0 4px
cursor: pointer
.search_facet_remove
left: 1px
top: 3px
.VS-icon-cancel
right: 24px
.oe_vs_unfold_drawer
position: absolute
top: 0
right: 0
height: 100%
line-height: 23px
padding: 0 7px 0 4px
color: #4c4c4c
.oe_searchview_facet
position: relative
cursor: pointer
&:before
border: 1px solid $facets-border
@include radius(3px)
background: #8786b7
-webkit-font-smoothing: auto
padding-left: 1.1em
// spacing for opera, FF
margin: 1px 0
&:focus
border-color: $facets-border-selected
@include box-shadow(0 0 3px 1px $facets-border-selected)
.oe_facet_values
background: #f0f0fa
@include radius(0 3px 3px 0)
.oe_facet_category, .oe_facet_value
height: 24px
padding: 1px 0.1em
.oe_facet_category
color: white
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.4)
.oe_facet_value
border-left: 1px solid $facets-border
.oe_facet_remove
position: absolute
top: 9px
right: 8px
width: 0
height: 0
display: inline-block
content: ""
vertical-align: top
border-top: 5px solid #4c4c4c
border-left: 5px solid transparent
border-right: 5px solid transparent
@include opacity(0.5)
top: 0
left: 2px
color: white
&:hover
color: #ccc
&.oe_searchview_open_drawer
.oe_searchview_drawer
display: block
.oe_searchview_unfold_drawer:before
content: ""
.oe_searchview_drawer
position: absolute
z-index: 1
// detach drawer from field slightly
margin-top: 3px
top: 100%
right: 0
background-color: white
width: 480px
min-width: 100%
display: none
border: 1px solid #ccc
text-align: left
padding-bottom: 0.5em
@include radius(1em)
> div
border-top: 1px solid #ccc
margin: 3px 0
> div:first-child
border-top: none
margin: 0
h4, h4 *
margin: 0
cursor: pointer
h4:before
content: ""
button, .button
border: none
background: transparent
padding: 0 2px
@include box-shadow(none)
@include radius(0)
.oe_searchview_filters
display: table
@ -1065,24 +1091,44 @@ $colour4: #8a89ba
li
list-style: none
padding: 3px 6px
height: 14px
padding: 3px 6px 3px 18px
line-height: 14px
color: inherit
cursor: pointer
&.oe_selected
background: url(/web/static/src/img/icons/gtk-apply.png) left 2px no-repeat
// after oe_selected so background color is not overridden
&:hover
background-color: #f0f0fa
background-color: $hover-background
.oe_searchview_custom
form
display: none
button
@include text-to-icon("S")
li
cursor: pointer
position: relative
line-height: 1.2em
padding: 2px 20px 2px 25px
&.oe_searchview_custom_private
background: url(/web/static/src/img/icons/terp-locked.png) 5px center no-repeat
&:hover
background-color: $hover-background
button
position: absolute
top: 0
right: 5px
.oe_searchview_advanced
h4
border-top: 1px solid #ccc
margin: 5px 0 3px
cursor: pointer
h4:before
content: ""
div
form
display: none
button.oe_add_condition:before
@ -1096,12 +1142,17 @@ $colour4: #8a89ba
li
list-style: none
margin: 0
white-space: nowrap
&.oe_opened
h4:before
content: ""
div
display: block
.oe_opened
h4:before
content: ""
form
display: block
// delete buttons
.oe_searchview_custom_delete, .searchview_extended_delete_prop
@include text-to-icon("d")
// }}}
// Views Common {{{
.oe_view_nocontent

View File

@ -111,7 +111,7 @@ instance.web.format_value = function (value, descriptor, value_if_empty) {
return value_if_empty === undefined ? '' : value_if_empty;
}
var l10n = _t.database.parameters;
switch (descriptor.type || (descriptor.field && descriptor.field.type)) {
switch (descriptor.widget || descriptor.type || (descriptor.field && descriptor.field.type)) {
case 'id':
return value.toString();
case 'integer':
@ -171,7 +171,7 @@ instance.web.parse_value = function (value, descriptor, value_if_empty) {
case "":
return value_if_empty === undefined ? false : value_if_empty;
}
switch (descriptor.type || (descriptor.field && descriptor.field.type)) {
switch (descriptor.widget || descriptor.type || (descriptor.field && descriptor.field.type)) {
case 'integer':
var tmp;
do {

File diff suppressed because it is too large Load Diff

View File

@ -615,6 +615,17 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
case 'edit':
this.do_edit_resource($option.data('model'), $option.data('id'), { name : $option.text() });
break;
case 'manage_filters':
this.do_action({
res_model: 'ir.filters',
views: [[false, 'list'], [false, 'form']],
type: 'ir.actions.act_window',
context: {
search_default_my_filters: true,
search_default_model_id: this.dataset.model
}
});
break;
default:
if (val) {
console.log("No debug handler for ", val);

View File

@ -264,32 +264,6 @@
</div>
</t>
<t t-name="WebClient">
<table class="oe_webclient">
<tr>
<td colspan="2" class="oe_topbar">
<div class="oe_menu_placeholder"/>
<div class="oe_user_menu_placeholder"/>
<div class="oe_systray"/>
</td>
</tr>
<tr>
<td class="oe_leftbar" valign="top">
<t t-js="d">
d.url = '/' + (window.location.search || '');
</t>
<a t-att-href="url" class="oe_logo"><img t-att-src='_s + "/web/static/src/img/logo.png"'/></a>
<div class="oe_secondary_menus_container"/>
<div class="oe_footer">
Powered by <a href="http://www.openerp.com"><span>Open</span>ERP</a>
</div>
</td>
<td class="oe_application">
</td>
</tr>
</table>
</t>
<t t-name="Menu">
<ul class="oe_menu" t-if="widget.data">
<li t-foreach="widget.data.data.children" t-as="menu">
@ -409,7 +383,10 @@
</tr>
<tr>
<td class="oe_leftbar" valign="top">
<a href="#" class="oe_logo"><img t-att-src='_s + "/web/static/src/img/logo.png"'/></a>
<t t-js="d">
d.url = '/' + (window.location.search || '');
</t>
<a t-att-href="url" class="oe_logo"><img t-att-src='_s + "/web/static/src/img/logo.png"'/></a>
<div class="oe_secondary_menus_container"/>
<div class="oe_footer">
Powered by <a href="http://www.openerp.com"><span>Open</span>ERP</a>
@ -476,6 +453,7 @@
<option t-if="view_manager.active_view === 'form'" value="toggle_layout_outline">Toggle Form Layout Outline</option>
<option value="fields">View Fields</option>
<option value="fvg">Fields View Get</option>
<option value="manage_filters">Manage Filters</option>
<t t-if="view_manager.session.uid === 1">
<option value="manage_views">Manage Views</option>
<option value="edit" data-model="ir.ui.view" t-att-data-id="view.fields_view.view_id">Edit <t t-esc="_.str.capitalize(view.fields_view.type)"/>View</option>
@ -998,11 +976,12 @@
</div>
</t>
<t t-name="web.datetimepicker">
<t t-set="placeholder" t-value="widget.getParent().node and widget.getParent().node.attrs.placeholder"/>
<div class="oe_datepicker_root oe_form_field_datetime">
<input type="text" class="oe_datepicker_container" disabled="disabled" style="display: none;"/>
<input type="text"
t-att-name="widget.name"
t-att-placeholder="widget.getParent().node.attrs.placeholder"
t-att-placeholder="placeholder"
t-attf-class="oe_datepicker_master field_#{widget.type_of_date} oe_form_field_#{widget.type_of_date}"
/><img class="oe_input_icon oe_datepicker_trigger" t-att-src='_s + "/web/static/src/img/ui/field_calendar.png"'
title="Select date" width="16" height="16" border="0"/>
@ -1318,7 +1297,31 @@
</t>
<div t-name="SearchView" class="oe_searchview">
<div class="oe_searchview_facets"/>
<div class="oe_searchview_clear"/>
<div class="oe_searchview_unfold_drawer"/>
<div class="oe_searchview_drawer"/>
</div>
<div t-name="SearchView.InputView"
class="oe_searchview_input"
contenteditable="true"/>
<!-- tabindex: makes div focusable -->
<div t-name="SearchView.FacetView"
class="oe_searchview_facet"
tabindex="0"
><span class="oe_facet_remove"></span
><span class="oe_facet_category oe_i" t-if="widget.model.has('icon')">
<t t-esc="widget.model.get('icon')"/>
</span
><span class="oe_facet_category" t-if="!widget.model.has('icon')">
<t t-esc="widget.model.get('category')"/>
</span ><span class="oe_facet_values"
/></div>
<span t-name="SearchView.FacetView.Value" class="oe_facet_value">
<t t-esc="widget.model.get('label')"/>
</span>
<t t-name="SearchView.managed-filters">
<option class="oe-filters-title" value="">Filters</option>
<optgroup label="-- Filters --">
@ -1375,8 +1378,9 @@
<t t-esc="attrs.string"/>
</button>
<ul t-name="SearchView.filters">
<li t-foreach="widget.filters" t-as="filter">
<t t-esc="filter.attrs.string or filter.attrs.name or 'Ω'"/>
<li t-foreach="widget.filters" t-as="filter"
t-att-title="filter.attrs.string ? filter.attrs.help : undefined">
<t t-esc="filter.attrs.string or filter.attrs.help or filter.attrs.name or 'Ω'"/>
</li>
</ul>
<t t-name="SearchView.filters.facet">
@ -1471,8 +1475,22 @@
</t>
</t>
</t>
<div t-name="SearchView.Filters" class="oe_searchview_filters">
</div>
<div t-name="SearchView.CustomFilters" class="oe_searchview_custom">
<ul class="oe_searchview_custom_list"/>
<h4 class="oe_searchview_custom_title">
<label for="oe_searchview_custom_input">Save search</label></h4>
<form>
<input id="oe_searchview_custom_input"/>
<button>Save</button><br/>
<label for="oe_searchview_custom_public">Share with all users:</label>
<input id="oe_searchview_custom_public" type="checkbox"/>
</form>
</div>
<div t-name="SearchView.advanced" class="oe_searchview_advanced">
<h4>Advanced Search...</h4>
<h4>Advanced Search</h4>
<form>
<ul>
@ -1494,26 +1512,23 @@
</select>
<select class="searchview_extended_prop_op"/>
<span class="searchview_extended_prop_value"/>
<a class="searchview_extended_delete_prop"
href="javascript:void(0)"><span> </span></a>
<button type="button" class="searchview_extended_delete_prop">Delete</button>
</li>
</t>
<t t-name="SearchView.extended_search.proposition.char">
<input t-att-id="widget.element_id" class="field_char"/>
<input class="field_char"/>
</t>
<t t-name="SearchView.extended_search.proposition.empty">
<span t-att-id="widget.element_id"></span>
<span/>
</t>
<t t-name="SearchView.extended_search.proposition.integer">
<input type="number" t-att-id="widget.element_id" class="field_integer" step="1"/>
<input type="number" class="field_integer" step="1"/>
</t>
<t t-name="SearchView.extended_search.proposition.float">
<input type="number" t-att-id="widget.element_id" class="field_float" step="0.01"/>
</t>
<t t-name="SearchView.extended_search.proposition.boolean">
<input type="number" class="field_float" step="0.01"/>
</t>
<t t-name="SearchView.extended_search.proposition.selection">
<select t-att-id="widget.element_id">
<select>
<t t-foreach="widget.field.selection" t-as="element">
<option t-att-value="element[0]"><t t-esc="element[1]"/></option>
</t>

View File

@ -66,7 +66,7 @@ $(document).ready(function () {
ok(!fail2);
});
asyncTest('Resolve all correctly ordered, sync', 1, function () {
asyncTest('Resolve all correctly ordered, async', 1, function () {
var dm = new openerp.web.DropMisordered();
var d1 = $.Deferred(), d2 = $.Deferred(),
@ -80,7 +80,7 @@ $(document).ready(function () {
ok(true);
});
});
asyncTest("Don't resolve mis-ordered, sync", 4, function () {
asyncTest("Don't resolve mis-ordered, async", 4, function () {
var dm = new openerp.web.DropMisordered(),
done1 = false, done2 = false,
fail1 = false, fail2 = false;
@ -104,7 +104,7 @@ $(document).ready(function () {
ok(!fail2);
}, 400);
});
asyncTest('Fail mis-ordered flag, sync', 4, function () {
asyncTest('Fail mis-ordered flag, async', 4, function () {
var dm = new openerp.web.DropMisordered(true),
done1 = false, done2 = false,
fail1 = false, fail2 = false;

File diff suppressed because it is too large Load Diff

View File

@ -10,9 +10,10 @@
<script src="/web/static/lib/underscore/underscore.js" type="text/javascript"></script>
<script src="/web/static/lib/underscore/underscore.string.js" type="text/javascript"></script>
<script src="/web/static/lib/backbone/backbone.js" type="text/javascript"></script>
<!-- jquery -->
<script src="/web/static/lib/jquery/jquery-1.6.4.js"></script>
<script src="/web/static/lib/jquery/jquery-1.7.2b1.js"></script>
<script src="/web/static/lib/jquery.ui/js/jquery-ui-1.8.17.custom.min.js"></script>
<script src="/web/static/lib/jquery.ba-bbq/jquery.ba-bbq.js"></script>
@ -53,4 +54,5 @@
<script type="text/javascript" src="/web/static/test/formats.js"></script>
<script type="text/javascript" src="/web/static/test/rpc.js"></script>
<script type="text/javascript" src="/web/static/test/evals.js"></script>
<script type="text/javascript" src="/web/static/test/search.js"></script>
</html>

View File

@ -247,6 +247,7 @@ texinfo_documents = [
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
todo_include_todos = True
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {

View File

@ -16,14 +16,14 @@ Contents:
async
rpc
search-view
Older stuff
-----------
.. toctree::
:maxdepth: 2
search-view
getting-started
production
widgets

View File

@ -13,217 +13,388 @@ multiple fields). The goal for this change is twofold:
* Improve the looks and behaviors of the view, and the fit within
OpenERP Web's new design.
The faceted search is implemented through a monkey-patched
The internal structure of the faceted search is inspired by
`VisualSearch <http://documentcloud.github.com/visualsearch/>`_
[#]_. VisualSearch is based on `Backbone
<http://documentcloud.github.com/backbone/>`_ and makes significant
use of Backbone's models and views. As a result, understanding the
implementation of the OpenERP Web 6.2 search view also requires a
basic understanding of Backbone.
[#previous]_.
As does VisualSearch, the new search view is based on `Backbone`_ and
makes significant use of Backbone's models and collections (OpenERP
Web's widgets make a good replacement for Backbone's own views). As a
result, understanding the implementation details of the OpenERP Web 7
search view also requires a basic understanding of Backbone's models,
collections and events.
.. note::
This document may mention *fetching* data. This is a shortcut for
"returning a deferred to [whatever is being fetched]". Unless
further noted, the function or method may opt to return nothing by
fetching ``null`` (which can easily be done by returning
``$.when(null)``, which simply wraps the ``null`` in a Deferred).
"returning a :js:class:`Deferred` to [whatever is being
fetched]". Unless further noted, the function or method may opt to
return nothing by fetching ``null`` (which can easily be done by
returning ``$.when(null)``, which simply wraps the ``null`` in a
Deferred).
Interaction between the Search View and VisualSearch
----------------------------------------------------
Working with the search view: creating new inputs
-------------------------------------------------
The core data abstraction in VisualSearch is
:js:class:`VS.model.SearchQuery`, a backbone Collection holding
instances of the :js:class:`VS.model.SearchFacet` backbone Model.
The primary component of search views, as with all other OpenERP
views, are inputs. The search view has two types of inputs — filters
and fields — but only one is easly customizable: fields.
Backbone models can hold any number of informal properties interacted
with through the :js:func:`~Backbone.Model.get` and
:js:func:`~Backbone.Model.set` methods. VisualSearch reserves three
such properties for its behavior, these properties *must* be correctly
set on all search facets created programmatically:
The mapping from OpenERP field types (and widgets) to search view
objects is stored in the ``openerp.web.search.fields``
:js:class:`~openerp.web.Registry` where new field types and widgets
can be added.
``app``
a reference to the VisualSearch instance using this facet. In the
search view, this instance is available as the
:js:attr:`~openerp.web.SearchView.vs` attribute to the searchview
instance.
Search view inputs have four main roles:
``category``
the *name* of the facet, displayed in the first section of a facet
view.
Loading defaults
++++++++++++++++
``value``
the *displayed value* of the facet, it is directly printed to the
right of the category.
Once the search view has initialized all its inputs, it will call
:js:func:`~openerp.web.search.Input.facet_for_defaults` on each input,
passing it a mapping (a javascript object) of ``name:value`` extracted
from the action's context.
The search view uses additional keys to store state and data it needs
to associate with facet objects:
This method should fetch a :js:class:`~openerp.web.search.Facet` (or
an equivalent object) for the field's default value if applicable (if
a default value for the field is found in the ``defaults`` mapping).
``field``
the search field instance which created the facet, used when the
search view needs to serialize the facets.
A default implementation is provided which checks if ``defaults``
contains a non-falsy value for the field's ``@name`` and calls
:js:func:`openerp.web.search.Input.facet_for` with that value.
``json``
the "logical" value of the facet, can be absent if the logical and
"printable" values of the facet are the same (e.g. for a basic text
field).
There is no default implementation of
:js:func:`openerp.web.search.Input.facet_for` [#no_impl]_, but
:js:class:`openerp.web.search.Field` provides one, which uses the
value as-is to fetch a :js:class:`~openerp.web.search.Facet`.
This value may be a complex javascript object such as an array (the
name stands for json-compatible value, it is not a JSON-encoded
string).
Providing completions
+++++++++++++++++++++
.. note::
An important component of the new search view is the auto-completion
pane, and the task of providing completion items is delegated to
inputs through the :js:func:`~openerp.web.search.Input.complete`
method.
in order to simplify getting the logical value of a search facet
model, :js:class:`VS.model.SearchFacet` has been extended with a
:js:func:`~VS.model.SearchFacet.value` method
This method should take a single argument (the string being typed by
the user) and should fetch an ``Array`` of possible completions
[#completion]_.
Extensions and patches to VisualSearch
++++++++++++++++++++++++++++++++++++++
A default implementation is provided which fetches nothing.
.. js:function:: VS.model.SearchFacet.value()
A completion item is a javascript object with two keys (technically it
can have any number of keys, but only these two will be used by the
search view):
Bundles the logic of selecting between ``json`` and ``value`` in
order to get the logical value of a facet.
``label``
.. js:attribute:: VS.options.callbacks.make_facet
The string which will be displayed in the completion pane. It may
be formatted using HTML (inline only), as a result if ``value`` is
interpolated into it it *must* be escaped. ``_.escape`` can be
used for this.
Called by :js:class:`VS.ui.SearchBox` when it needs to create a
new search facet *view*. By default this is not supported by
VisualSearch, and requires monkey-patching
:js:func:`VS.ui.SearchBox.renderFacet`.
``facet``
This patch should not alter any behavior if
:js:attr:`~VS.options.callbacks.make_facet` is not used.
Either a :js:class:`~openerp.web.search.Facet` object or (more
commonly) the corresponding attributes object. This is the facet
which will be inserted into the search query if the completion
item is selected by the user.
.. js:attribute:: VS.options.callbacks.make_input
If the ``facet`` is not provided (not present, ``null``, ``undefined``
or any other falsy value), the completion item will not be selectable
and will act as a section title of sort (the ``label`` will be
formatted differently). If an input *may* fetch multiple completion
items, it *should* prefix those with a section title using its own
name. This has no technical consequence but is clearer for users.
Similar to :js:attr:`~VS.options.callbacks.make_facet`, but called
when the :js:class:`~VS.ui.SearchBox` needs to create a search
input view. It requires monkey-patching
:js:func:`VS.ui.SearchBox.renderSearchInput`.
Providing drawer/supplementary UI
+++++++++++++++++++++++++++++++++
Finally, :js:func:`VS.ui.SearchBox.searchEvent` is monkey-patched to
get rid of its serialize/load round-tripping of facet data: the
additional attributes needed by the search view don't round-trip (at
all) so VisualSearch must not load any data from its (fairly
simplistic) text-serialization format.
For some inputs (fields or not), interaction via autocompletion may be
awkward or even impossible.
.. note::
These may opt to being rendered in a "drawer" as well or instead. In
that case, they will undergo the normal widget lifecycle and be
rendered inside the drawer.
a second issue is that — as of `commit 3fca87101d`_ — VisualSearch
correctly serializes facet categories containing spaces but is
unable to load them back in. It also does not handle facets with
*empty* categories correctly.
.. Found no good type-based way to handle this, since there is no MI
(so no type-tagging) and it's possible for both Field and non-Field
input to be put into the drawer, for whatever reason (e.g. some
sort of auto-detector completion item for date widgets, but a
second more usual calendar widget in the drawer for more
obvious/precise interactions)
Loading Defaults
----------------
Any input can note its desire to be rendered in the drawer by
returning a truthy value from
:js:func:`~openerp.web.search.Input.in_drawer`.
After loading the view data, the SearchView will call
:js:func:`openerp.web.search.Input.facet_for_defaults` on each of its
inputs with the ``defaults`` mapping of key:values (where each key
corresponds to an input). This method should look into the
``defaults`` mapping and fetch the field's default value as a
:js:class:`~VS.models.SearchFacet` if applicable.
By default, :js:func:`~openerp.web.search.Input.in_drawer` returns the
value of :js:attr:`~openerp.web.search.Input._in_drawer`, which is
``false``. The behavior can be toggled either by redefining the
attribute to ``true`` (either on the class or on the input), or by
overriding :js:func:`~openerp.web.search.Input.in_drawer` itself.
The default implementation is to check if there is a default value for
the current input's name (via
:js:attr:`openerp.web.search.Input.attrs.name`) and if there is to
convert this value to a :js:class:`~VS.models.SearchFacet` by calling
:js:func:`openerp.web.search.Input.facet_for`.
The input will be rendered in the full width of the drawer, it will be
started only once (per view).
There is no built-in (default) implementation of
:js:func:`openerp.web.search.Input.facet_for`. This method should
fetch the :js:class:`~VS.models.SearchFacet` corresponding to the
"raw" value passed as argument.
.. todo:: drawer API (if a widget wants to close the drawer in some
way), part of the low-level SearchView API/interactions?
Providing auto-completion
-------------------------
An important component of the unified search view is the faceted
autocompletion pane. In order to provide good user and developer
experiences, this pane is pluggable (value-wise): each and every
control of the search view can check for (and provide) categorized
auto-completions for a given value being typed by the user.
.. todo:: handle filters and filter groups via a "driver" input which
dynamically collects, lays out and renders filters? =>
exercises drawer thingies
This is done by implementing
:js:func:`openerp.web.search.Input.complete`: the method is provided
with a value to complete, and should fetch an ``Array`` of completion
values. These completion values will then be provided to the global
autocompletion list, implemented via `jquery-ui autocomplete
<http://jqueryui.com/demos/autocomplete/>`_.
Programmatic interactions: internal model
-----------------------------------------
Because the search view uses a custom renderer for its completion, it
was possible to fix some incompatibilities between the attributes of
completion items and VisualSearch's facet model:
This new searchview is built around an instance of
:js:class:`~openerp.web.search.SearchQuery` available as
:js:attr:`openerp.web.SearchView.query`.
Actual completion items
+++++++++++++++++++++++
The query is a `backbone collection`_ of
:js:class:`~openerp.web.search.Facet` objects, which can be interacted
with directly by external objects or search view controls
(e.g. widgets displayed in the drawer).
These are selectable items, and upon selection are turned into actual
search facet objects. They should have all the properties of a search
facet (as described above) and can have one more optional property:
``label``.
.. js:class:: openerp.web.search.SearchQuery
When rendering an item in the list, the renderer will first try to use
the ``label`` property if it exists (``label`` can contain HTML and
will be inserted as-is, so it can bold or emphasize some of its
elements), if it does not the ``value`` property will be used.
The current search query of the search view, provides convenience
behaviors for manipulating :js:class:`~openerp.web.search.Facet`
on top of the usual `backbone collection`_ methods.
.. note:: the ``app`` key should not be specified on completion item,
it will be set automatically when the search view creates
the facet from the item.
The query ensures all of its facets contain at least one
:js:class:`~openerp.web.search.FacetValue` instance. Otherwise,
the facet is automatically removed from the query.
Section titles
++++++++++++++
.. js:function:: openerp.web.search.SearchQuery.add(values, options)
A second kind of completion values is the section titles. Section
titles are similar to completion items but only have a ``category``
property. They will be rendered in a different style and can not be
selected in the auto-completion (they will be skipped).
Overridden from the base ``add`` method so that adding a facet
which is *already* in the collection will merge the value of
the new facet into the old one rather than add a second facet
with different values.
.. note::
:param values: facet, facet attributes or array thereof
:returns: the collection itself
Technically, section title items can have any property they want
*as long as they do not have a value property*. A ``value``
property set to ``false``, ``null`` or ``undefined`` is **not**
equivalent to not having a ``value`` property.
.. js:function:: openerp.web.search.SearchQuery.toggle(value, options)
If an input *may* fetch more than one completion item, it *should*
prepend a section title (using its own name) to the completion items.
Convenience method for toggling facet values in a query:
removes the values (through the facet itself) if they are
present, adds them if they are not. If the facet itself is not
in the collection, adds it automatically.
A toggling is atomic: only one change event will be triggered
on the facet regardless of the number of values added to or
removed from the facet (if the facet already exists), and the
facet is only removed from the query if it has no value *at
the end* of the toggling.
:param value: facet or facet attributes
:returns: the collection
.. js:class:: openerp.web.search.Facet
A `backbone model`_ representing a single facet of the current
research. May map to a search field, or to a more complex or
fuzzier input (e.g. a custom filter or an advanced search).
.. js:attribute:: category
The displayed name of the facet, as a ``String``. This is a
backbone model attribute.
.. js:attribute:: field
The :js:class:`~openerp.web.search.Input` instance which
originally created the facet [#facet-field]_, used to delegate
some operations (such as serializing the facet's values to
domains and contexts). This is a backbone model attribute.
.. js:attribute:: values
:js:class:`~openerp.web.search.FacetValues` as a javascript
attribute, stores all the values for the facet and helps
propagate their events to the facet. Is also available as a
backbone attribute (via ``#get`` and ``#set``) in which cases
it serializes to and deserializes from javascript arrays (via
``Collection#toJSON`` and ``Collection#reset``).
.. js:attribute:: [icon]
optional, a single ASCII letter (a-z or A-Z) mapping to the
bundled mnmliconsRegular icon font.
When a facet with an ``icon`` attribute is rendered, the icon
is displayed (in the icon font) in the first section of the
facet instead of the ``category``.
By default, only filters make use of this facility.
.. js:class:: openerp.web.search.FacetValues
`Backbone collection`_ of
:js:class:`~openerp.web.search.FacetValue` instances.
.. js:class:: openerp.web.search.FacetValue
`Backbone model`_ representing a single value within a facet,
represents a pair of (displayed name, logical value).
.. js:attribute:: label
Backbone model attribute storing the "displayable"
representation of the value, visually output to the
user. Must be a string.
.. js:attribute:: value
Backbone model attribute storing the logical/internal value
(of itself), will be used by
:js:class:`~openerp.web.search.Input` to serialize to domains
and contexts.
Can be of any type.
Converting from facet objects
-----------------------------
Ultimately, the point of the search view is to allow searching. In
OpenERP this is done via :ref:`domains <openerpserver:domains>`. On
the other hand, the OpenERP Web 6.2 search view's state is modelled
after a collection of :js:class:`~VS.model.SearchFacet`, and each
the other hand, the OpenERP Web 7 search view's state is modelled
after a collection of :js:class:`~openerp.web.search.Facet`, and each
field of a search view may have special requirements when it comes to
the domains it produces [#]_.
the domains it produces [#special]_.
So there needs to be some way of mapping
:js:class:`~VS.model.SearchFacet` objects to OpenERP search data.
:js:class:`~openerp.web.search.Facet` objects to OpenERP search data.
This is done via an input's
:js:func:`~openerp.web.search.Input.get_domain` and
:js:func:`~openerp.web.search.Input.get_context`. Each takes a
:js:class:`~VS.model.SearchFacet` and returns whatever it's supposed
to generate (a domain or a context, respectively). Either can return
``null`` if the current value does not map to a domain or context, and
can throw an :js:class:`~openerp.web.search.Invalid` exception if the
value is not valid at all for the field.
:js:class:`~openerp.web.search.Facet` and returns whatever it's
supposed to generate (a domain or a context, respectively). Either can
return ``null`` if the current value does not map to a domain or
context, and can throw an :js:class:`~openerp.web.search.Invalid`
exception if the value is not valid at all for the field.
Converting to facet objects
---------------------------
.. note::
The :js:class:`~openerp.web.search.Facet` object can have any
number of values (from 1 upwards)
.. note::
There is a third conversion method,
:js:func:`~openerp.web.search.Input.get_groupby`, which returns an
``Array`` of groupby domains rather than a single context. At this
point, it is only implemented on (and used by) filters.
Field services
++++++++++++++
:js:class:`~openerp.web.search.Field` provides a default
implementation of :js:func:`~openerp.web.search.Input.get_domain` and
:js:func:`~openerp.web.search.Input.get_context` taking care of most
of the peculiarities pertaining to OpenERP's handling of fields in
search views. It also provides finer hooks to let developers of new
fields and widgets customize the behavior they want without
necessarily having to reimplement all of
:js:func:`~openerp.web.search.Input.get_domain` or
:js:func:`~openerp.web.search.Input.get_context`:
.. js:function:: openerp.web.search.Field.get_context(facet)
If the field has no ``@context``, simply returns
``null``. Otherwise, calls
:js:func:`~openerp.web.search.Field.value_from` once for each
:js:class:`~openerp.web.search.FacetValue` of the current
:js:class:`~openerp.web.search.Facet` (in order to extract the
basic javascript object from the
:js:class:`~openerp.web.search.FacetValue` then evaluates
``@context`` with each of these values set as ``self``, and
returns the union of all these contexts.
:param facet:
:type facet: openerp.web.search.Facet
:returns: a context (literal or compound)
.. js:function:: openerp.web.search.Field.get_domain(facet)
If the field has no ``@filter_domain``, calls
:js:func:`~openerp.web.search.Field.make_domain` once with each
:js:class:`~openerp.web.search.FacetValue` of the current
:js:class:`~openerp.web.search.Facet` as well as the field's
``@name`` and either its ``@operator`` or
:js:attr:`~openerp.web.search.Field.default_operator`.
If the field has an ``@filter_value``, calls
:js:func:`~openerp.web.search.Field.value_from` once per
:js:class:`~openerp.web.search.FacetValue` and evaluates
``@filter_value`` with each of these values set as ``self``.
In either case, "ors" all of the resulting domains (using ``|``)
if there is more than one
:js:class:`~openerp.web.search.FacetValue` and returns the union
of the result.
:param facet:
:type facet: openerp.web.search.Facet
:returns: a domain (literal or compound)
.. js:function:: openerp.web.search.Field.make_domain(name, operator, facetValue)
Builds a literal domain from the provided data. Calls
:js:func:`~openerp.web.search.Field.value_from` on the
:js:class:`~openerp.web.search.FacetValue` and evaluates and sets
it as the domain's third value, uses the other two parameters as
the first two values.
Can be overridden to build more complex default domains.
:param String name: the field's name
:param String operator: the operator to use in the field's domain
:param facetValue:
:type facetValue: openerp.web.search.FacetValue
:returns: Array<(String, String, Object)>
.. js:function:: openerp.web.search.Field.value_from(facetValue)
Extracts a "bare" javascript value from the provided
:js:class:`~openerp.web.search.FacetValue`, and returns it.
The default implementation will simply return the ``value``
backbone property of the argument.
:param facetValue:
:type facetValue: openerp.web.search.FacetValue
:returns: Object
.. js:attribute:: openerp.web.search.Field.default_operator
Operator used to build a domain when a field has no ``@operator``
or ``@filter_domain``. ``"="`` for
:js:class:`~openerp.web.search.Field`
Arbitrary data storage
++++++++++++++++++++++
:js:class:`~openerp.web.search.Facet` and
:js:class:`~openerp.web.search.FacetValue` objects (and structures)
provided by your widgets should never be altered by the search view
(or an other widget). This means you are free to add arbitrary fields
in these structures if you need to (because you have more complex
needs than the attributes described in this document).
Ideally this should be avoided, but the possibility remains.
Changes
-------
.. todo:: merge in changelog instead
.. todo:: merge in changelog instead?
The displaying of the search view was significantly altered from
OpenERP Web 6.1 to OpenERP Web 6.2.
OpenERP Web 6.1 to OpenERP Web 7.
As a result, while the external API used to interact with the search
view does not change many internal details — including the interaction
@ -247,11 +418,11 @@ Widgets API
* :js:func:`~openerp.web.search.Input.clear` has been removed since
clearing the search view now simply consists of removing all search
facets from VisualSearch
facets
* :js:func:`~openerp.web.search.Input.get_domain` and
:js:func:`~openerp.web.search.Input.get_context` now take a
:js:class:`~VS.model.SearchFacet` as parameter, from which it's
:js:class:`~openerp.web.search.Facet` as parameter, from which it's
their job to get whatever value they want
* :js:func:`~openerp.web.search.Input.get_groupby` has been added. It returns
@ -271,19 +442,16 @@ Filters
Fields
++++++
* ``get_value`` now takes a :js:class:`~VS.model.SearchFacet` (instead
of taking no argument).
A default implementation is provided as
:js:func:`openerp.web.search.Field.get_value` and simply calls
:js:func:`VS.model.SearchFacet.value`.
* ``get_value`` has been replaced by
:js:func:`~openerp.web.search.Field.value_from` as it now takes a
:js:class:`~openerp.web.search.FacetValue` argument (instead of no
argument). It provides a default implementation returning the
``value`` property of its argument.
* The third argument to
:js:func:`~openerp.web.search.Field.make_domain` is now the
:js:class:`~VS.model.SearchFacet` received by
:js:func:`~openerp.web.search.Field.get_domain`, so child classes
have all the information they need to derive the "right" resulting
domain.
:js:func:`~openerp.web.search.Field.make_domain` is now a
:js:class:`~openerp.web.search.FacetValue` so child classes have all
the information they need to derive the "right" resulting domain.
Custom filters
++++++++++++++
@ -300,12 +468,82 @@ Many To One
:js:func:`openerp.web.search.ManyToOneField.setup_autocomplete` has
been removed.
.. [#] the library code is untouched, all patching is performed in the
Search view's implementation module. Changes to the
VisualSearch code should only update the library to new
revisions or releases.
.. [#] search view fields may also bundle context data to add to the
search context
Advanced Search
+++++++++++++++
* The advanced search is now a more standard
:js:class:`~openerp.web.search.Input` configured to be rendered in
the drawer.
* :js:class:`~openerp.web.search.ExtendedSearchProposition.Field` are
now standard widgets, with the "right" behaviors (they don't rebind
their ``$element`` in ``start()``)
* The ad-hoc optional setting of the openerp field descriptor on a
:js:class:`~openerp.web.search.ExtendedSearchProposition.Field` has
been removed, the field descriptor is now passed as second argument
to the
:js:class:`~openerp.web.search.ExtendedSearchProposition.Field`'s
constructor, and bound to its
:js:attr:`~openerp.web.search.ExtendedSearchProposition.Field.field`.
* Instead of its former domain triplet ``(field, operator, value)``,
:js:func:`~openerp.web.search.ExtendedSearchProposition.get_proposition`
now returns an object with two fields ``label`` and ``value``,
respectively a human-readable version of the proposition and the
corresponding domain triplet for the proposition.
.. [#previous]
the original view was implemented on top of a monkey-patched
VisualSearch, but as our needs diverged from VisualSearch's goal
this made less and less sense ultimately leading to a clean-room
reimplementation
.. [#no_impl]
In case you are extending the search view with a brand new type of
input
.. [#completion]
Ideally this array should not hold more than about 10 items, but
the search view does not put any constraint on this at the
moment. Note that this may change.
.. [#facet-field]
``field`` does not actually need to be an instance of
:js:class:`~openerp.web.search.Input`, nor does it need to be what
created the facet, it just needs to provide the three
facet-serialization methods
:js:func:`~openerp.web.search.Input.get_domain`,
:js:func:`~openerp.web.search.Input.get_context` and
:js:func:`~openerp.web.search.Input.get_gropuby`, existing
:js:class:`~openerp.web.search.Input` subtypes merely provide
convenient base implementation for those methods.
Complex search view inputs (especially those living in the drawer)
may prefer using object literals with the right slots returning
closed-over values or some other scheme un-bound to an actual
:js:class:`~openerp.web.search.Input`, as
:js:class:`~openerp.web.search.CustomFilters` and
:js:class:`~openerp.web.search.Advanced` do.
.. [#special]
search view fields may also bundle context data to add to the
search context
.. _Backbone:
http://documentcloud.github.com/backbone/
.. _Backbone.Collection:
.. _Backbone collection:
http://documentcloud.github.com/backbone/#Collection
.. _Backbone model:
http://documentcloud.github.com/backbone/#Model
.. _commit 3fca87101d:
https://github.com/documentcloud/visualsearch/commit/3fca87101d
https://github.com/documentcloud/visualsearch/commit/3fca87101d