diff --git a/addons/web/__openerp__.py b/addons/web/__openerp__.py index 26ee5613dc5..417fea91434 100644 --- a/addons/web/__openerp__.py +++ b/addons/web/__openerp__.py @@ -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", diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 12ef0b53524..6eb8ed94909 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -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) diff --git a/addons/web/static/lib/qweb/qweb2.js b/addons/web/static/lib/qweb/qweb2.js index d4f7781e88b..c9b065ee69f 100644 --- a/addons/web/static/lib/qweb/qweb2.js +++ b/addons/web/static/lib/qweb/qweb2.js @@ -4,7 +4,7 @@ // TODO: t-set + t-value + children node == scoped variable ? var QWeb2 = { expressions_cache: {}, - RESERVED_WORDS: 'true,false,NaN,null,undefined,debugger,console,in,instanceof,new,function,return,this,typeof,eval,Math,RegExp,Array,Object,Date'.split(','), + RESERVED_WORDS: 'true,false,NaN,null,undefined,debugger,console,in,instanceof,new,function,return,this,typeof,eval,void,Math,RegExp,Array,Object,Date'.split(','), ACTIONS_PRECEDENCE: 'foreach,if,call,set,esc,escf,raw,rawf,js,debug,log'.split(','), WORD_REPLACEMENT: { 'and': '&&', diff --git a/addons/web/static/lib/visualsearch/LICENSE b/addons/web/static/lib/visualsearch/LICENSE deleted file mode 100755 index fe0ea1154c4..00000000000 --- a/addons/web/static/lib/visualsearch/LICENSE +++ /dev/null @@ -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. \ No newline at end of file diff --git a/addons/web/static/lib/visualsearch/README b/addons/web/static/lib/visualsearch/README deleted file mode 100755 index c059a9824d6..00000000000 --- a/addons/web/static/lib/visualsearch/README +++ /dev/null @@ -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 \ No newline at end of file diff --git a/addons/web/static/lib/visualsearch/Rakefile b/addons/web/static/lib/visualsearch/Rakefile deleted file mode 100755 index 192a50b944e..00000000000 --- a/addons/web/static/lib/visualsearch/Rakefile +++ /dev/null @@ -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 diff --git a/addons/web/static/lib/visualsearch/assets.yml b/addons/web/static/lib/visualsearch/assets.yml deleted file mode 100755 index 357c37c74aa..00000000000 --- a/addons/web/static/lib/visualsearch/assets.yml +++ /dev/null @@ -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 \ No newline at end of file diff --git a/addons/web/static/lib/visualsearch/build/visualsearch-datauri.css b/addons/web/static/lib/visualsearch/build/visualsearch-datauri.css deleted file mode 100755 index 3e3edd0063f..00000000000 --- a/addons/web/static/lib/visualsearch/build/visualsearch-datauri.css +++ /dev/null @@ -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; - } diff --git a/addons/web/static/lib/visualsearch/build/visualsearch.css b/addons/web/static/lib/visualsearch/build/visualsearch.css deleted file mode 100755 index 079ff122300..00000000000 --- a/addons/web/static/lib/visualsearch/build/visualsearch.css +++ /dev/null @@ -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; - } diff --git a/addons/web/static/lib/visualsearch/build/visualsearch.js b/addons/web/static/lib/visualsearch/build/visualsearch.js deleted file mode 100755 index 319d978db3c..00000000000 --- a/addons/web/static/lib/visualsearch/build/visualsearch.js +++ /dev/null @@ -1,1845 +0,0 @@ -// This is the annotated source code for -// [VisualSearch.js](http://documentcloud.github.com/visualsearch/), -// a rich search box for real data. -// -// The annotated source HTML is generated by -// [Docco](http://jashkenas.github.com/docco/). - -/** @license VisualSearch.js 0.2.2 - * (c) 2011 Samuel Clay, @samuelclay, DocumentCloud Inc. - * VisualSearch.js may be freely distributed under the MIT license. - * For all details and documentation: - * http://documentcloud.github.com/visualsearch - */ - -(function() { - - var $ = jQuery; // Handle namespaced jQuery - - // Setting up VisualSearch globals. These will eventually be made instance-based. - if (!window.VS) window.VS = {}; - if (!VS.app) VS.app = {}; - if (!VS.ui) VS.ui = {}; - if (!VS.model) VS.model = {}; - if (!VS.utils) VS.utils = {}; - - // Sets the version for VisualSearch to be used programatically elsewhere. - VS.VERSION = '0.2.2'; - - VS.VisualSearch = function(options) { - var defaults = { - container : '', - query : '', - unquotable : [], - callbacks : { - search : $.noop, - focus : $.noop, - blur : $.noop, - facetMatches : $.noop, - valueMatches : $.noop - } - }; - this.options = _.extend({}, defaults, options); - this.options.callbacks = _.extend({}, defaults.callbacks, options.callbacks); - - VS.app.hotkeys.initialize(); - this.searchQuery = new VS.model.SearchQuery(); - this.searchBox = new VS.ui.SearchBox({app: this}); - - if (options.container) { - var searchBox = this.searchBox.render().el; - $(this.options.container).html(searchBox); - } - this.searchBox.value(this.options.query || ''); - - // Disable page caching for browsers that incorrectly cache the visual search inputs. - // This is forced the browser to re-render the page when it is retrieved in its history. - $(window).bind('unload', function(e) {}); - - // Gives the user back a reference to the `searchBox` so they - // can use public methods. - return this; - }; - - // Entry-point used to tie all parts of VisualSearch together. It will either attach - // itself to `options.container`, or pass back the `searchBox` so it can be rendered - // at will. - VS.init = function(options) { - return new VS.VisualSearch(options); - }; - -})(); -(function() { - -var $ = jQuery; // Handle namespaced jQuery - -// The search box is responsible for managing the many facet views and input views. -VS.ui.SearchBox = Backbone.View.extend({ - - id : 'search', - - events : { - 'click .VS-cancel-search-box' : 'clearSearch', - 'mousedown .VS-search-box' : 'maybeFocusSearch', - 'dblclick .VS-search-box' : 'highlightSearch', - 'click .VS-search-box' : 'maybeTripleClick' - }, - - // Creating a new SearchBox registers handlers for re-rendering facets when necessary, - // as well as handling typing when a facet is selected. - initialize : function() { - this.app = this.options.app; - this.flags = { - allSelected : false - }; - this.facetViews = []; - this.inputViews = []; - _.bindAll(this, 'renderFacets', '_maybeDisableFacets', 'disableFacets', - 'deselectAllFacets', 'addedFacet', 'removedFacet', 'changedFacet'); - this.app.searchQuery - .bind('reset', this.renderFacets) - .bind('add', this.addedFacet) - .bind('remove', this.removedFacet) - .bind('change', this.changedFacet); - $(document).bind('keydown', this._maybeDisableFacets); - }, - - // Renders the search box, but requires placement on the page through `this.el`. - render : function() { - $(this.el).append(JST['search_box']({})); - $(document.body).setMode('no', 'search'); - - return this; - }, - - // # Querying Facets # - - // Either gets a serialized query string or sets the faceted query from a query string. - value : function(query) { - if (query == null) return this.serialize(); - return this.setQuery(query); - }, - - // Uses the VS.app.searchQuery collection to serialize the current query from the various - // facets that are in the search box. - serialize : function() { - var query = []; - var inputViewsCount = this.inputViews.length; - - this.app.searchQuery.each(_.bind(function(facet, i) { - query.push(this.inputViews[i].value()); - query.push(facet.serialize()); - }, this)); - - if (inputViewsCount) { - query.push(this.inputViews[inputViewsCount-1].value()); - } - - return _.compact(query).join(' '); - }, - - // Takes a query string and uses the SearchParser to parse and render it. Note that - // `VS.app.SearchParser` refreshes the `VS.app.searchQuery` collection, which is bound - // here to call `this.renderFacets`. - setQuery : function(query) { - this.currentQuery = query; - VS.app.SearchParser.parse(this.app, query); - }, - - // Returns the position of a facet/input view. Useful when moving between facets. - viewPosition : function(view) { - var views = view.type == 'facet' ? this.facetViews : this.inputViews; - var position = _.indexOf(views, view); - if (position == -1) position = 0; - return position; - }, - - // Used to launch a search. Hitting enter or clicking the search button. - searchEvent : function(e) { - var query = this.value(); - this.focusSearch(e); - this.value(query); - this.app.options.callbacks.search(query, this.app.searchQuery); - }, - - // # Rendering Facets # - - // Add a new facet. Facet will be focused and ready to accept a value. Can also - // specify position, in the case of adding facets from an inbetween input. - addFacet : function(category, initialQuery, position) { - category = VS.utils.inflector.trim(category); - initialQuery = VS.utils.inflector.trim(initialQuery || ''); - if (!category) return; - - var model = new VS.model.SearchFacet({ - category : category, - value : initialQuery || '', - app : this.app - }); - this.app.searchQuery.add(model, {at: position}); - }, - - // Renders a newly added facet, and selects it. - addedFacet : function (model) { - this.renderFacets(); - var facetView = _.detect(this.facetViews, function(view) { - if (view.model == model) return true; - }); - - _.defer(function() { - facetView.enableEdit(); - }); - }, - - // Changing a facet programmatically re-renders it. - changedFacet: function () { - this.renderFacets(); - }, - - // When removing a facet, potentially do something. For now, the adjacent - // remaining facet is selected, but this is handled by the facet's view, - // since its position is unknown by the time the collection triggers this - // remove callback. - removedFacet : function (facet, query, options) {}, - - // Renders each facet as a searchFacet view. - renderFacets : function() { - this.facetViews = []; - this.inputViews = []; - - this.$('.VS-search-inner').empty(); - - this.app.searchQuery.each(_.bind(function(facet, i) { - this.renderFacet(facet, i); - }, this)); - - // Add on an n+1 empty search input on the very end. - this.renderSearchInput(); - }, - - // Render a single facet, using its category and query value. - renderFacet : function(facet, position) { - var view = new VS.ui.SearchFacet({ - app : this.app, - model : facet, - order : position - }); - - // Input first, facet second. - this.renderSearchInput(); - this.facetViews.push(view); - this.$('.VS-search-inner').children().eq(position*2).after(view.render().el); - - view.calculateSize(); - _.defer(_.bind(view.calculateSize, view)); - - return view; - }, - - // Render a single input, used to create and autocomplete facets - renderSearchInput : function() { - var input = new VS.ui.SearchInput({position: this.inputViews.length, app: this.app}); - this.$('.VS-search-inner').append(input.render().el); - this.inputViews.push(input); - }, - - // # Modifying Facets # - - // Clears out the search box. Command+A + delete can trigger this, as can a cancel button. - // - // If a `clearSearch` callback was provided, the callback is invoked and - // provided with a function performs the actual removal of the data. This - // allows third-party developers to either clear data asynchronously, or - // prior to performing their custom "clear" logic. - clearSearch : function(e) { - var actualClearSearch = _.bind(function() { - this.disableFacets(); - this.value(''); - this.flags.allSelected = false; - this.searchEvent(e); - this.focusSearch(e); - }, this); - - if (this.app.options.callbacks.clearSearch) { - this.app.options.callbacks.clearSearch(actualClearSearch); - } else { - actualClearSearch(); - } - }, - - // Command+A selects all facets. - selectAllFacets : function() { - this.flags.allSelected = true; - - $(document).one('click.selectAllFacets', this.deselectAllFacets); - - _.each(this.facetViews, function(facetView, i) { - facetView.selectFacet(); - }); - _.each(this.inputViews, function(inputView, i) { - inputView.selectText(); - }); - }, - - // Used by facets and input to see if all facets are currently selected. - allSelected : function(deselect) { - if (deselect) this.flags.allSelected = false; - return this.flags.allSelected; - }, - - // After `selectAllFacets` is engaged, this method is bound to the entire document. - // This immediate disables and deselects all facets, but it also checks if the user - // has clicked on either a facet or an input, and properly selects the view. - deselectAllFacets : function(e) { - this.disableFacets(); - - if (this.$(e.target).is('.category,input')) { - var el = $(e.target).closest('.search_facet,.search_input'); - var view = _.detect(this.facetViews.concat(this.inputViews), function(v) { - return v.el == el[0]; - }); - if (view.type == 'facet') { - view.selectFacet(); - } else if (view.type == 'input') { - _.defer(function() { - view.enableEdit(true); - }); - } - } - }, - - // Disables all facets except for the passed in view. Used when switching between - // facets, so as not to have to keep state of active facets. - disableFacets : function(keepView) { - _.each(this.inputViews, function(view) { - if (view && view != keepView && - (view.modes.editing == 'is' || view.modes.selected == 'is')) { - view.disableEdit(); - } - }); - _.each(this.facetViews, function(view) { - if (view && view != keepView && - (view.modes.editing == 'is' || view.modes.selected == 'is')) { - view.disableEdit(); - view.deselectFacet(); - } - }); - - this.flags.allSelected = false; - this.removeFocus(); - $(document).unbind('click.selectAllFacets'); - }, - - // Resize all inputs to account for extra keystrokes which may be changing the facet - // width incorrectly. This is a safety check to ensure inputs are correctly sized. - resizeFacets : function(view) { - _.each(this.facetViews, function(facetView, i) { - if (!view || facetView == view) { - facetView.resize(); - } - }); - }, - - // Handles keydown events on the document. Used to complete the Cmd+A deletion, and - // blurring focus. - _maybeDisableFacets : function(e) { - if (this.flags.allSelected && VS.app.hotkeys.key(e) == 'backspace') { - e.preventDefault(); - this.clearSearch(e); - return false; - } else if (this.flags.allSelected && VS.app.hotkeys.printable(e)) { - this.clearSearch(e); - } - }, - - // # Focusing Facets # - - // Move focus between facets and inputs. Takes a direction as well as many options - // for skipping over inputs and only to facets, placement of cursor position in facet - // (i.e. at the end), and selecting the text in the input/facet. - focusNextFacet : function(currentView, direction, options) { - options = options || {}; - var viewCount = this.facetViews.length; - var viewPosition = options.viewPosition || this.viewPosition(currentView); - - if (!options.skipToFacet) { - // Correct for bouncing between matching text and facet arrays. - if (currentView.type == 'text' && direction > 0) direction -= 1; - if (currentView.type == 'facet' && direction < 0) direction += 1; - } else if (options.skipToFacet && currentView.type == 'text' && - viewCount == viewPosition && direction >= 0) { - // Special case of looping around to a facet from the last search input box. - viewPosition = 0; - direction = 0; - } - var view, next = Math.min(viewCount, viewPosition + direction); - - if (currentView.type == 'text') { - if (next >= 0 && next < viewCount) { - view = this.facetViews[next]; - } else if (next == viewCount) { - view = this.inputViews[this.inputViews.length-1]; - } - if (view && options.selectFacet && view.type == 'facet') { - view.selectFacet(); - } else if (view) { - view.enableEdit(); - view.setCursorAtEnd(direction || options.startAtEnd); - } - } else if (currentView.type == 'facet') { - if (options.skipToFacet) { - if (next >= viewCount || next < 0) { - view = _.last(this.inputViews); - view.enableEdit(); - } else { - view = this.facetViews[next]; - view.enableEdit(); - view.setCursorAtEnd(direction || options.startAtEnd); - } - } else { - view = this.inputViews[next]; - view.enableEdit(); - } - } - if (options.selectText) view.selectText(); - this.resizeFacets(); - }, - - maybeFocusSearch : function(e) { - if ($(e.target).is('.VS-search-box') || - $(e.target).is('.VS-search-inner') || - e.type == 'keydown') { - this.focusSearch(e); - } - }, - - // Bring focus to last input field. - focusSearch : function(e, selectText) { - var view = this.inputViews[this.inputViews.length-1]; - view.enableEdit(selectText); - if (!selectText) view.setCursorAtEnd(-1); - if (e.type == 'keydown') { - view.keydown(e); - view.box.trigger('keydown'); - } - _.defer(_.bind(function() { - if (!this.$('input:focus').length) { - view.enableEdit(selectText); - } - }, this)); - }, - - // Double-clicking on the search wrapper should select the existing text in - // the last search input. Also start the triple-click timer. - highlightSearch : function(e) { - if ($(e.target).is('.VS-search-box') || - $(e.target).is('.VS-search-inner') || - e.type == 'keydown') { - var lastinput = this.inputViews[this.inputViews.length-1]; - lastinput.startTripleClickTimer(); - this.focusSearch(e, true); - } - }, - - maybeTripleClick : function(e) { - var lastinput = this.inputViews[this.inputViews.length-1]; - return lastinput.maybeTripleClick(e); - }, - - // Used to show the user is focused on some input inside the search box. - addFocus : function() { - this.app.options.callbacks.focus(); - this.$('.VS-search-box').addClass('VS-focus'); - }, - - // User is no longer focused on anything in the search box. - removeFocus : function() { - this.app.options.callbacks.blur(); - var focus = _.any(this.facetViews.concat(this.inputViews), function(view) { - return view.isFocused(); - }); - if (!focus) this.$('.VS-search-box').removeClass('VS-focus'); - }, - - // Show a menu which adds pre-defined facets to the search box. This is unused for now. - showFacetCategoryMenu : function(e) { - e.preventDefault(); - e.stopPropagation(); - if (this.facetCategoryMenu && this.facetCategoryMenu.modes.open == 'is') { - return this.facetCategoryMenu.close(); - } - - var items = [ - {title: 'Account', onClick: _.bind(this.addFacet, this, 'account', '')}, - {title: 'Project', onClick: _.bind(this.addFacet, this, 'project', '')}, - {title: 'Filter', onClick: _.bind(this.addFacet, this, 'filter', '')}, - {title: 'Access', onClick: _.bind(this.addFacet, this, 'access', '')} - ]; - - var menu = this.facetCategoryMenu || (this.facetCategoryMenu = new dc.ui.Menu({ - items : items, - standalone : true - })); - - this.$('.VS-icon-search').after(menu.render().open().content); - return false; - } - -}); - -})(); - -(function() { - -var $ = jQuery; // Handle namespaced jQuery - -// This is the visual search facet that holds the category and its autocompleted -// input field. -VS.ui.SearchFacet = Backbone.View.extend({ - - type : 'facet', - - className : 'search_facet', - - events : { - 'click .category' : 'selectFacet', - 'keydown input' : 'keydown', - 'mousedown input' : 'enableEdit', - 'mouseover .VS-icon-cancel' : 'showDelete', - 'mouseout .VS-icon-cancel' : 'hideDelete', - 'click .VS-icon-cancel' : 'remove' - }, - - initialize : function(options) { - this.flags = { - canClose : false - }; - _.bindAll(this, 'set', 'keydown', 'deselectFacet', 'deferDisableEdit'); - }, - - // Rendering the facet sets up autocompletion, events on blur, and populates - // the facet's input with its starting value. - render : function() { - $(this.el).html(JST['search_facet']({ - model : this.model - })); - - this.setMode('not', 'editing'); - this.setMode('not', 'selected'); - this.box = this.$('input'); - this.box.val(this.model.get('value')); - this.box.bind('blur', this.deferDisableEdit); - // Handle paste events with `propertychange` - this.box.bind('input propertychange', this.keydown); - this.setupAutocomplete(); - - return this; - }, - - // This method is used to setup the facet's input to auto-grow. - // This is defered in the searchBox so it can be attached to the - // DOM to get the correct font-size. - calculateSize : function() { - this.box.autoGrowInput(); - this.box.unbind('updated.autogrow'); - this.box.bind('updated.autogrow', _.bind(this.moveAutocomplete, this)); - }, - - // Forces a recalculation of this facet's input field's value. Called when - // the facet is focused, removed, or otherwise modified. - resize : function(e) { - this.box.trigger('resize.autogrow', e); - }, - - // Watches the facet's input field to see if it matches the beginnings of - // words in `autocompleteValues`, which is different for every category. - // If the value, when selected from the autocompletion menu, is different - // than what it was, commit the facet and search for it. - setupAutocomplete : function() { - this.box.autocomplete({ - source : _.bind(this.autocompleteValues, this), - minLength : 0, - delay : 0, - autoFocus : true, - position : {offset : "0 5"}, - create : _.bind(function(e, ui) { - $(this.el).find('.ui-autocomplete-input').css('z-index','auto'); - }, this), - select : _.bind(function(e, ui) { - e.preventDefault(); - var originalValue = this.model.get('value'); - this.set(ui.item.value); - if (originalValue != ui.item.value || this.box.val() != ui.item.value) { - this.search(e); - } - return false; - }, this), - open : _.bind(function(e, ui) { - var box = this.box; - this.box.autocomplete('widget').find('.ui-menu-item').each(function() { - var $value = $(this); - if ($value.data('item.autocomplete')['value'] == box.val()) { - box.data('autocomplete').menu.activate(new $.Event("mouseover"), $value); - } - }); - }, this) - }); - - this.box.autocomplete('widget').addClass('VS-interface'); - }, - - // As the facet's input field grows, it may move to the next line in the - // search box. `autoGrowInput` triggers an `updated` event on the input - // field, which is bound to this method to move the autocomplete menu. - moveAutocomplete : function() { - var autocomplete = this.box.data('autocomplete'); - if (autocomplete) { - autocomplete.menu.element.position({ - my : "left top", - at : "left bottom", - of : this.box.data('autocomplete').element, - collision : "flip", - offset : "0 5" - }); - } - }, - - // When a user enters a facet and it is being edited, immediately show - // the autocomplete menu and size it to match the contents. - searchAutocomplete : function(e) { - var autocomplete = this.box.data('autocomplete'); - if (autocomplete) { - var menu = autocomplete.menu.element; - autocomplete.search(); - - // Resize the menu based on the correctly measured width of what's bigger: - // the menu's original size or the menu items' new size. - menu.outerWidth(Math.max( - menu.width('').outerWidth(), - autocomplete.element.outerWidth() - )); - } - }, - - // Closes the autocomplete menu. Called on disabling, selecting, deselecting, - // and anything else that takes focus out of the facet's input field. - closeAutocomplete : function() { - var autocomplete = this.box.data('autocomplete'); - if (autocomplete) autocomplete.close(); - }, - - // Search terms used in the autocomplete menu. These are specific to the facet, - // and only match for the facet's category. The values are then matched on the - // first letter of any word in matches, and finally sorted according to the - // value's own category. You can pass `preserveOrder` as an option in the - // `facetMatches` callback to skip any further ordering done client-side. - autocompleteValues : function(req, resp) { - var category = this.model.get('category'); - var value = this.model.get('value'); - var searchTerm = req.term; - - this.options.app.options.callbacks.valueMatches(category, searchTerm, function(matches, options) { - options = options || {}; - matches = matches || []; - - if (searchTerm && value != searchTerm) { - if (options.preserveMatches) { - return matches; - } else { - var re = VS.utils.inflector.escapeRegExp(searchTerm || ''); - var matcher = new RegExp('\\b' + re, 'i'); - matches = $.grep(matches, function(item) { - return matcher.test(item) || - matcher.test(item.value) || - matcher.test(item.label); - }); - } - } - - if (options.preserveOrder) { - resp(matches); - } else { - resp(_.sortBy(matches, function(match) { - if (match == value || match.value == value) return ''; - else return match; - })); - } - }); - - }, - - // Sets the facet's model's value. - set : function(value) { - if (!value) return; - this.model.set({'value': value}); - }, - - // Before the searchBox performs a search, we need to close the - // autocomplete menu. - search : function(e, direction) { - if (!direction) direction = 1; - this.closeAutocomplete(); - this.options.app.searchBox.searchEvent(e); - _.defer(_.bind(function() { - this.options.app.searchBox.focusNextFacet(this, direction, {viewPosition: this.options.order}); - }, this)); - }, - - // Begin editing the facet's input. This is called when the user enters - // the input either from another facet or directly clicking on it. - // - // This method tells all other facets and inputs to disable so it can have - // the sole focus. It also prepares the autocompletion menu. - enableEdit : function() { - if (this.modes.editing != 'is') { - this.setMode('is', 'editing'); - this.deselectFacet(); - if (this.box.val() == '') { - this.box.val(this.model.get('value')); - } - } - - this.flags.canClose = false; - this.options.app.searchBox.disableFacets(this); - this.options.app.searchBox.addFocus(); - _.defer(_.bind(function() { - this.options.app.searchBox.addFocus(); - }, this)); - this.resize(); - this.searchAutocomplete(); - this.box.focus(); - }, - - // When the user blurs the input, they may either be going to another input - // or off the search box entirely. If they go to another input, this facet - // will be instantly disabled, and the canClose flag will be turned back off. - // - // However, if the user clicks elsewhere on the page, this method starts a timer - // that checks if any of the other inputs are selected or are being edited. If - // not, then it can finally close itself and its autocomplete menu. - deferDisableEdit : function() { - this.flags.canClose = true; - _.delay(_.bind(function() { - if (this.flags.canClose && !this.box.is(':focus') && - this.modes.editing == 'is' && this.modes.selected != 'is') { - this.disableEdit(); - } - }, this), 250); - }, - - // Called either by other facets receiving focus or by the timer in `deferDisableEdit`, - // this method will turn off the facet, remove any text selection, and close - // the autocomplete menu. - disableEdit : function() { - var newFacetQuery = VS.utils.inflector.trim(this.box.val()); - if (newFacetQuery != this.model.get('value')) { - this.set(newFacetQuery); - } - this.flags.canClose = false; - this.box.selectRange(0, 0); - this.box.blur(); - this.setMode('not', 'editing'); - this.closeAutocomplete(); - this.options.app.searchBox.removeFocus(); - }, - - // Selects the facet, which blurs the facet's input and highlights the facet. - // If this is the only facet being selected (and not part of a select all event), - // we attach a mouse/keyboard watcher to check if the next action by the user - // should delete this facet or just deselect it. - selectFacet : function(e) { - if (e) e.preventDefault(); - var allSelected = this.options.app.searchBox.allSelected(); - if (this.modes.selected == 'is') return; - - if (this.box.is(':focus')) { - this.box.setCursorPosition(0); - this.box.blur(); - } - - this.flags.canClose = false; - this.closeAutocomplete(); - this.setMode('is', 'selected'); - this.setMode('not', 'editing'); - if (!allSelected || e) { - $(document).unbind('keydown.facet', this.keydown); - $(document).unbind('click.facet', this.deselectFacet); - _.defer(_.bind(function() { - $(document).unbind('keydown.facet').bind('keydown.facet', this.keydown); - $(document).unbind('click.facet').one('click.facet', this.deselectFacet); - }, this)); - this.options.app.searchBox.disableFacets(this); - this.options.app.searchBox.addFocus(); - } - return false; - }, - - // Turns off highlighting on the facet. Called in a variety of ways, this - // only deselects the facet if it is selected, and then cleans up the - // keyboard/mouse watchers that were created when the facet was first - // selected. - deselectFacet : function(e) { - if (e) e.preventDefault(); - if (this.modes.selected == 'is') { - this.setMode('not', 'selected'); - this.closeAutocomplete(); - this.options.app.searchBox.removeFocus(); - } - $(document).unbind('keydown.facet', this.keydown); - $(document).unbind('click.facet', this.deselectFacet); - return false; - }, - - // Is the user currently focused in this facet's input field? - isFocused : function() { - return this.box.is(':focus'); - }, - - // Hovering over the delete button styles the facet so the user knows that - // the delete button will kill the entire facet. - showDelete : function() { - $(this.el).addClass('search_facet_maybe_delete'); - }, - - // On `mouseout`, the user is no longer hovering on the delete button. - hideDelete : function() { - $(this.el).removeClass('search_facet_maybe_delete'); - }, - - // When switching between facets, depending on the direction the cursor is - // coming from, the cursor in this facet's input field should match the original - // direction. - setCursorAtEnd : function(direction) { - if (direction == -1) { - this.box.setCursorPosition(this.box.val().length); - } else { - this.box.setCursorPosition(0); - } - }, - - // Deletes the facet and sends the cursor over to the nearest input field. - remove : function(e) { - var committed = this.model.get('value'); - this.deselectFacet(); - this.disableEdit(); - this.options.app.searchQuery.remove(this.model); - if (committed) { - this.search(e, -1); - } else { - this.options.app.searchBox.renderFacets(); - this.options.app.searchBox.focusNextFacet(this, -1, {viewPosition: this.options.order}); - } - }, - - // Selects the text in the facet's input field. When the user tabs between - // facets, convention is to highlight the entire field. - selectText: function() { - this.box.selectRange(0, this.box.val().length); - }, - - // Handles all keyboard inputs when in the facet's input field. This checks - // for movement between facets and inputs, entering a new value that needs - // to be autocompleted, as well as the removal of this facet. - keydown : function(e) { - var key = VS.app.hotkeys.key(e); - - if (key == 'enter' && this.box.val()) { - this.disableEdit(); - this.search(e); - } else if (key == 'left') { - if (this.modes.selected == 'is') { - this.deselectFacet(); - this.options.app.searchBox.focusNextFacet(this, -1, {startAtEnd: -1}); - } else if (this.box.getCursorPosition() == 0 && !this.box.getSelection().length) { - this.selectFacet(); - } - } else if (key == 'right') { - if (this.modes.selected == 'is') { - e.preventDefault(); - this.deselectFacet(); - this.setCursorAtEnd(0); - this.enableEdit(); - } else if (this.box.getCursorPosition() == this.box.val().length) { - e.preventDefault(); - this.disableEdit(); - this.options.app.searchBox.focusNextFacet(this, 1); - } - } else if (VS.app.hotkeys.shift && key == 'tab') { - e.preventDefault(); - this.options.app.searchBox.focusNextFacet(this, -1, { - startAtEnd : -1, - skipToFacet : true, - selectText : true - }); - } else if (key == 'tab') { - e.preventDefault(); - this.options.app.searchBox.focusNextFacet(this, 1, { - skipToFacet : true, - selectText : true - }); - } else if (VS.app.hotkeys.command && (e.which == 97 || e.which == 65)) { - e.preventDefault(); - this.options.app.searchBox.selectAllFacets(); - return false; - } else if (VS.app.hotkeys.printable(e) && this.modes.selected == 'is') { - this.options.app.searchBox.focusNextFacet(this, -1, {startAtEnd: -1}); - this.remove(e); - } else if (key == 'backspace') { - if (this.modes.selected == 'is') { - e.preventDefault(); - this.remove(e); - } else if (this.box.getCursorPosition() == 0 && - !this.box.getSelection().length) { - e.preventDefault(); - this.selectFacet(); - } - } - - this.resize(e); - - // Handle paste events - if (e.which == null) { - this.searchAutocomplete(e); - _.defer(_.bind(this.resize, this, e)); - } - } - -}); - -})(); - -(function() { - -var $ = jQuery; // Handle namespaced jQuery - -// This is the visual search input that is responsible for creating new facets. -// There is one input placed in between all facets. -VS.ui.SearchInput = Backbone.View.extend({ - - type : 'text', - - className : 'search_input', - - events : { - 'keypress input' : 'keypress', - 'keydown input' : 'keydown', - 'click input' : 'maybeTripleClick', - 'dblclick input' : 'startTripleClickTimer' - }, - - initialize : function() { - this.app = this.options.app; - this.flags = { - canClose : false - }; - _.bindAll(this, 'removeFocus', 'addFocus', 'moveAutocomplete', 'deferDisableEdit'); - }, - - // Rendering the input sets up autocomplete, events on focusing and blurring - // the input, and the auto-grow of the input. - render : function() { - $(this.el).html(JST['search_input']({})); - - this.setMode('not', 'editing'); - this.setMode('not', 'selected'); - this.box = this.$('input'); - this.box.autoGrowInput(); - this.box.bind('updated.autogrow', this.moveAutocomplete); - this.box.bind('blur', this.deferDisableEdit); - this.box.bind('focus', this.addFocus); - this.setupAutocomplete(); - - return this; - }, - - // Watches the input and presents an autocompleted menu, taking the - // remainder of the input field and adding a separate facet for it. - // - // See `addTextFacetRemainder` for explanation on how the remainder works. - setupAutocomplete : function() { - this.box.autocomplete({ - minLength : 1, - delay : 50, - autoFocus : true, - position : {offset : "0 -1"}, - source : _.bind(this.autocompleteValues, this), - create : _.bind(function(e, ui) { - $(this.el).find('.ui-autocomplete-input').css('z-index','auto'); - }, this), - select : _.bind(function(e, ui) { - e.preventDefault(); - e.stopPropagation(); - var remainder = this.addTextFacetRemainder(ui.item.value); - var position = this.options.position + (remainder ? 1 : 0); - this.app.searchBox.addFacet(ui.item.value, '', position); - return false; - }, this) - }); - - // Renders the results grouped by the categories they belong to. - this.box.data('autocomplete')._renderMenu = function(ul, items) { - var category = ''; - _.each(items, _.bind(function(item, i) { - if (item.category && item.category != category) { - ul.append('
- 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. -
- -- The complete annotated source code - is also available. -
- -- The project is - hosted on GitHub. - You can report bugs and discuss features on the - issues page, - on Freenode in the #documentcloud channel, - or send tweets to @documentcloud. -
- -- VisualSearch.js is an open-source component of DocumentCloud. -
-- - Created by Samuel Clay, - @samuelclay. - -
- -- 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. -
- -- Here's an example of a search on DocumentCloud.org that uses facets. -
- -- The project is - hosted on GitHub. - You can report bugs and discuss features on the - issues page, - on Freenode in the #documentcloud channel, - or send tweets to @documentcloud. -
- -
- VisualSearch.js is an open-source component of DocumentCloud.
-
- The complete annotated source code
- is also available.
-
0. Everything (visualsearch.zip) | -|
---|---|
Download everything | -|
1. VisualSearch JavaScript (visualsearch.js) | -|
Production Version (0.2.2) | -8kb, Minified and Gzipped | -
Development Version (0.2.2) | -45kb, Uncompressed with Comments | -
2. VisualSearch Stylesheets (visualsearch.css) | -|
You should include both the datauri and image urls versions. See how to include both | -|
Production Version - datauri | -4kb, Minified and Gzipped | -
Production Version - image urls | -4kb, Minified and Gzipped | -
Development Version | -8kb, Uncompressed with Comments | -
3. VisualSearch Images | -|
Search Glyph | -4kb, embedded in visualsearch-datauri.css | -
Cancel Button | -4kb, embedded in visualsearch-datauri.css | -
4. VisualSearch Dependencies (jQuery 1.4+, jQuery UI, Underscore.js, Backbone.js) | -|
You should only include the dependencies you don't already have. | -|
Production Version - All | -49kb, Minified and Gzipped | -
Development Version - All | -340kb, Uncompressed with Comments | -
jQuery 1.6.1 | -238kb, Uncompressed with Comments | -
jQuery UI 1.8.13: Core Position Widget Autocomplete |
- 48kb, Uncompressed with Comments | -
Underscore 1.1.5 | -29kb, Uncompressed with Comments | -
Backbone 0.5.0 | -41kb, Uncompressed with Comments | -
To use VisualSearch.js on your site, follow these instructions on installation, configuration, and customization.
- -<script src="visualsearch.js" type="text/javascript"></script>
-<!--[if (!IE)|(gte IE 8)]><!-->
- <link href="visualsearch-datauri.css" media="screen" rel="stylesheet" type="text/css" />
-<!--<![endif]-->
-<!--[if lte IE 7]><!-->
- <link href="visualsearch.css" media="screen" rel="stylesheet" type="text/css" />
-<!--<![endif]-->
- <div class="visual_search"></div>
-
-<script type="text/javascript" charset="utf-8">
- $(document).ready(function() {
- var visualSearch = VS.init({
- container : $('.visual_search'),
- query : '',
- callbacks : {
- search : function(query, searchCollection) {},
- facetMatches : function(callback) {},
- valueMatches : function(facet, searchTerm, callback) {}
- }
- });
- });
-</script>
- 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;
- }
- }
-...
-}
- // 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<country:"South Africa">,
-// FacetModel<account:5-samuel>,
-// FacetModel<title:"Pentagon Papers">]
-
-// Set the search query with raw text
-visualSearch.searchBox.value("Country: US State: \"New York\" Key: Value")
-
-
-
- 0.2.2 March 10th, 2012
- If you do not want to automatically filter the value matches, you can pass an
- options hash with preserveMatches: true as the second argument to the callback.
- See pull request #44 for details.
-
- 0.2.1 November 14th, 2011
- The autocompleted facets and values that are provided by your callbacks facetMatches
- and valueMatches can now preserve the order of items you give them. Simply pass an
- options hash with preserveOrder: true as the second argument to the callback. See
- the demo page for an example.
-
- 0.2.0 August 10th, 2011
- Multiple instances of VisualSearch on a single page. VS.init now returns
- a reference to the instance. The search callback now contains both the
- serialized search query and a reference to the search query collection (as a
- Backbone.Collection),
- which can be used to manipulate each facet directly. See
- the source code for search_query.js for available
- methods on the collection.
-
- 0.1.0 June 23rd, 2011
- Initial release of VisualSearch.js.
-
- - - -
- -