Add:
static/js/index.advanced-search-form.js, static/sass/index.sass,
static/css/lib/jqueryui.custom.min.css
-Add `jQuery UI` autocompletion menu to the advanced-search form's
language input field, to replace `twitter typeahead` (which was
incompatible with the new form's style rules).
static/js/index.advanced-search-form.js
-Add regex field specifier to query string.
tags/v1.0^2
| @@ -10,25 +10,14 @@ var searchGroups = $("div#search-groups"); | |||||
| (function loadInputFieldWidgets(){ | (function loadInputFieldWidgets(){ | ||||
| $(".search-group input#date-last-modified").datepicker(); | $(".search-group input#date-last-modified").datepicker(); | ||||
| $(".search-group input#date-created").datepicker(); | $(".search-group input#date-created").datepicker(); | ||||
| var languages = new Bloodhound({ | |||||
| datumTokenizer: Bloodhound.tokenizers.obj.whitespace("value"), | |||||
| queryTokenizer: Bloodhound.tokenizers.whitespace, | |||||
| local: $.map(TYPEAHEAD_LANGUAGES, function(state){ | |||||
| return {value : state}; | |||||
| }) | |||||
| }); | |||||
| languages.initialize(); | |||||
| $("#language.typeahead").typeahead({ | |||||
| hint: true, | |||||
| highlight: true, | |||||
| minLength: 1 | |||||
| }, | |||||
| { | |||||
| name: "languages", | |||||
| displayKey: "value", | |||||
| source: languages.ttAdapter() | |||||
| $("#autocomplete").autocomplete({ | |||||
| source: function(request, response){ | |||||
| var matcher = new RegExp( | |||||
| $.ui.autocomplete.escapeRegex(request.term), "i"); | |||||
| response($.grep(AUTOCOMPLETE_LANGUAGES, function(item){ | |||||
| return matcher.test(item); | |||||
| })); | |||||
| } | |||||
| }); | }); | ||||
| }()); | }()); | ||||
| @@ -36,18 +25,19 @@ var searchGroups = $("div#search-groups"); | |||||
| * Set all advanced search form button callbacks. | * Set all advanced search form button callbacks. | ||||
| */ | */ | ||||
| (function setSearchFormCallbacks(){ | (function setSearchFormCallbacks(){ | ||||
| // Create a new search group, and update the `#sidebar` checklist accordingly. | |||||
| // Create a new search group, and update the `#sidebar` checklist. | |||||
| $("button#add-group").click(function(){ | $("button#add-group").click(function(){ | ||||
| $("div#sidebar input[type=checkbox]").prop("checked", false); | $("div#sidebar input[type=checkbox]").prop("checked", false); | ||||
| searchGroups.children("#selected").removeAttr("id"); | searchGroups.children("#selected").removeAttr("id"); | ||||
| var searchGroup = $("<div/>", {class : "search-group", id : "selected"}); | var searchGroup = $("<div/>", {class : "search-group", id : "selected"}); | ||||
| searchGroups.append(searchGroup.append(createSearchGroupInput("language"))); | |||||
| searchGroups.append( | |||||
| searchGroup.append(createSearchGroupInput("language"))); | |||||
| $("div#sidebar input[type=checkbox]#language").prop("checked", true); | $("div#sidebar input[type=checkbox]#language").prop("checked", true); | ||||
| }); | }); | ||||
| // Remove the currently selected group if it's not the only one, and mark one | |||||
| // of its siblings as selected. | |||||
| // Remove the currently selected group if it's not the only one, and mark | |||||
| // one of its siblings as selected. | |||||
| $("button#remove-group").click(function(){ | $("button#remove-group").click(function(){ | ||||
| var currentGroup = $("div.search-group#selected"); | var currentGroup = $("div.search-group#selected"); | ||||
| @@ -96,7 +86,7 @@ var searchGroups = $("div#search-groups"); | |||||
| function createSearchGroupInput(fieldId){ | function createSearchGroupInput(fieldId){ | ||||
| return [ | return [ | ||||
| "<div id='" + fieldId + "'>", | "<div id='" + fieldId + "'>", | ||||
| "<div>" + fieldId.replace(/-/g, " ") + "</div>", | |||||
| "<div class='name'>" + fieldId.replace(/-/g, " ") + "</div>", | |||||
| "<input class='" + fieldId + "' name='" + fieldId + "'type='text'>", | "<input class='" + fieldId + "' name='" + fieldId + "'type='text'>", | ||||
| "<input type='checkbox' name='regex'><span>Regex</span>", | "<input type='checkbox' name='regex'><span>Regex</span>", | ||||
| "</div>" | "</div>" | ||||
| @@ -111,28 +101,35 @@ function assembleQuery(){ | |||||
| var groupQueries = []; | var groupQueries = []; | ||||
| for(var group = 0; group < groups.length; group++){ | for(var group = 0; group < groups.length; group++){ | ||||
| var inputs = groups[group].querySelectorAll("input[type=text]"); | |||||
| var groupQuery = [] | |||||
| for(var field = 0; field < inputs.length; field++) | |||||
| if(inputs[field].value.length > 0) | |||||
| groupQuery.push(genFieldQueryString(inputs[field])); | |||||
| var inputFields = groups[group].querySelectorAll("input[type=text]"); | |||||
| var regexCheckbox = groups[group].querySelectorAll("input[name=regex]"); | |||||
| var groupQuery = []; | |||||
| for(var field = 0; field < inputFields.length; field++) | |||||
| if(inputFields[field].value.length > 0) | |||||
| groupQuery.push(genFieldQueryString( | |||||
| inputFields[field], regexCheckbox[field].checked)); | |||||
| groupQueries.push(groupQuery.join(" AND ")); | groupQueries.push(groupQuery.join(" AND ")); | ||||
| } | } | ||||
| // console.log(groupQueries.join(" OR ")); | |||||
| console.log(groupQueries.join(" OR ")); | |||||
| } | } | ||||
| /* | /* | ||||
| * Generate a processed query string for an input field's value. | * Generate a processed query string for an input field's value. | ||||
| * | * | ||||
| * @param field An `input[type=text]` DOM element. | |||||
| * @param field (DOM element) An `input[type=text]` element. | |||||
| * @param hasRegex (boolean) Whether or not the field's value has regex. | |||||
| */ | */ | ||||
| function genFieldQueryString(field){ | |||||
| function genFieldQueryString(field, hasRegex){ | |||||
| var terms = field.value.replace(/\\/g, "\\\\").replace(/\"/g, "\\\""); | var terms = field.value.replace(/\\/g, "\\\\").replace(/\"/g, "\\\""); | ||||
| if(field.value.indexOf('"') >= 0) | |||||
| return '"' + field.getAttribute("name") + ":" + terms + '"'; | |||||
| return terms; | |||||
| var query = field.getAttribute("name") + ":" + ((hasRegex)?"re:":"") + terms; | |||||
| if(field.value.indexOf('"') >= 0){ | |||||
| // ['"', field.getAttribute("name"), ":", regex?"re:":"", terms, '"'] | |||||
| return '"' + query + '"'; | |||||
| } | |||||
| return query; | |||||
| } | } | ||||
| (function testQueryStringGeneration(){ | (function testQueryStringGeneration(){ | ||||
| @@ -1,42 +1,24 @@ | |||||
| /* | /* | ||||
| Stylesheet for index.html. | |||||
| Stylesheet for `index.html`. | |||||
| */ | */ | ||||
| @import mixins | @import mixins | ||||
| @import variables | @import variables | ||||
| $minSearchFieldsWidth: 490px | |||||
| .ui-datepicker | .ui-datepicker | ||||
| font-size: 70% | font-size: 70% | ||||
| .twitter-typeahead | |||||
| // display: inline-block | |||||
| width: 60% | |||||
| .tt-hint | |||||
| color: $baseColor2 | |||||
| .tt-dropdown-menu | |||||
| width: 100% | |||||
| padding: 4px 0 | |||||
| background-color: white | |||||
| border: 1px solid $baseColor2 | |||||
| .tt-suggestion | |||||
| padding: 4px | |||||
| p | |||||
| margin: 0 | |||||
| strong | |||||
| color: $baseColor1 | |||||
| .ui-autocomplete | |||||
| max-height: 30% | |||||
| overflow-x: hidden | |||||
| overflow-y: scroll | |||||
| padding: 0px | |||||
| &.tt-cursor | |||||
| @extend .t1 | |||||
| >li.ui-menu-item a.ui-state-focus | |||||
| @include vendor(transition, background-color 0.3s ease-out) | |||||
| background-color: $baseColor1 | |||||
| * | |||||
| color: white | |||||
| div#search-field | div#search-field | ||||
| @extend .t2 | @extend .t2 | ||||
| @@ -70,8 +52,6 @@ div#search-field | |||||
| color: $baseColor2 | color: $baseColor2 | ||||
| font-style: italic | font-style: italic | ||||
| $minSearchFieldsWidth: 490px | |||||
| form#search-bar | form#search-bar | ||||
| min-width: $minSearchFieldsWidth | min-width: $minSearchFieldsWidth | ||||
| @@ -114,178 +94,180 @@ div#search-field | |||||
| width: 26px | width: 26px | ||||
| height: 26px | height: 26px | ||||
| div#advanced-search | |||||
| background-color: white | |||||
| border: 1px solid $baseColor3 | |||||
| font-size: 96% | |||||
| height: 400px | |||||
| min-width: $minSearchFieldsWidth | |||||
| padding-top: 0px | |||||
| overflow-x: auto | |||||
| overflow-y: hidden | |||||
| #heading | |||||
| color: $baseColor2 | |||||
| display: block | |||||
| font-size: 120% | |||||
| padding-left: 1% | |||||
| padding-top: 1% | |||||
| width: 100% | |||||
| &.partly-visible | |||||
| margin-top: 0% | |||||
| padding-bottom: 3% | |||||
| position: absolute | |||||
| width: 100% | |||||
| div | |||||
| display: inline-block | |||||
| font-size: 110% | |||||
| #title | |||||
| position: absolute | |||||
| top: -1% | |||||
| left: 1% | |||||
| &#col1 | |||||
| width: 25% | |||||
| span | |||||
| font-size: 50% | |||||
| &#col2 | |||||
| width: 75% | |||||
| form#search-bar | |||||
| padding-top: 3% | |||||
| margin-left: auto | |||||
| margin-right: auto | |||||
| width: 60% | |||||
| >div | |||||
| @include vendor(box-sizing, border-box) | |||||
| div#advanced-search | |||||
| background-color: white | |||||
| border: 1px solid $baseColor3 | |||||
| display: none | |||||
| font-size: 96% | |||||
| height: 400px | |||||
| min-width: $minSearchFieldsWidth | |||||
| padding-top: 0px | |||||
| overflow-x: auto | |||||
| overflow-y: hidden | |||||
| #heading | |||||
| color: $baseColor2 | |||||
| display: block | |||||
| font-size: 120% | |||||
| padding-left: 1% | |||||
| padding-top: 1% | |||||
| width: 100% | |||||
| display: inline-block | |||||
| float: left | |||||
| div | |||||
| display: inline-block | |||||
| font-size: 110% | |||||
| #sidebar | |||||
| padding-left: 1% | |||||
| &#col1 | |||||
| width: 25% | width: 25% | ||||
| >ul | |||||
| list-style: none | |||||
| padding-left: 0 | |||||
| margin-bottom: 8% | |||||
| margin-top: 2% | |||||
| li | |||||
| margin-bottom: 2% | |||||
| label | |||||
| user-select: none | |||||
| div | |||||
| @extend .t3 | |||||
| background-color: $lightGray | |||||
| border: none | |||||
| padding: 3% | |||||
| width: 85% | |||||
| &:hover, &.selectedInputField | |||||
| @extend .t3 | |||||
| background-color: $baseColor2 | |||||
| color: white | |||||
| cursor: pointer | |||||
| width: 90% | |||||
| input[type="checkbox"] | |||||
| display: none | |||||
| &:checked + label > div | |||||
| @extend .selectedInputField | |||||
| background-color: $baseColor1 | |||||
| color: white | |||||
| width: 90% | |||||
| button#add-group | |||||
| background-color: $lightBlue | |||||
| border: none | |||||
| color: white | |||||
| display: block | |||||
| height: 40px | |||||
| margin-bottom: 2% | |||||
| overflow: hidden | |||||
| white-space: nowrap | |||||
| width: 40px | |||||
| span | |||||
| font-size: 150% | |||||
| font-weight: bold | |||||
| margin-left: 6px | |||||
| margin-right: 14px | |||||
| &#col2 | |||||
| width: 75% | |||||
| >div | |||||
| @include vendor(box-sizing, border-box) | |||||
| display: inline-block | |||||
| float: left | |||||
| &:hover | |||||
| #sidebar | |||||
| padding-left: 1% | |||||
| width: 25% | |||||
| >ul | |||||
| list-style: none | |||||
| padding-left: 0 | |||||
| margin-bottom: 8% | |||||
| margin-top: 2% | |||||
| li | |||||
| margin-bottom: 2% | |||||
| label | |||||
| user-select: none | |||||
| div | |||||
| @extend .t3 | @extend .t3 | ||||
| background-color: $blue | |||||
| cursor: pointer | |||||
| background-color: $lightGray | |||||
| border: none | |||||
| padding: 3% | |||||
| width: 85% | |||||
| &:hover, &.selectedInputField | |||||
| @extend .t3 | |||||
| background-color: $baseColor2 | |||||
| color: white | |||||
| cursor: pointer | |||||
| width: 90% | |||||
| input[type="checkbox"] | |||||
| display: none | |||||
| &:checked + label > div | |||||
| @extend .selectedInputField | |||||
| background-color: $baseColor1 | |||||
| color: white | |||||
| width: 90% | width: 90% | ||||
| button#remove-group | |||||
| @extend button#add-group | |||||
| button#add-group | |||||
| background-color: $lightBlue | |||||
| border: none | |||||
| color: white | |||||
| display: block | |||||
| height: 40px | |||||
| margin-bottom: 2% | |||||
| overflow: hidden | |||||
| white-space: nowrap | |||||
| width: 40px | |||||
| background-color: #D82D48 | |||||
| span | |||||
| font-size: 150% | |||||
| font-weight: bold | |||||
| margin-left: 6px | |||||
| margin-right: 14px | |||||
| span | |||||
| padding-left: 3px | |||||
| &:hover | |||||
| @extend .t3 | |||||
| &:hover | |||||
| @extend .t3 | |||||
| background-color: $blue | |||||
| cursor: pointer | |||||
| width: 90% | |||||
| background-color: #F11437 | |||||
| button#remove-group | |||||
| @extend button#add-group | |||||
| #search-groups | |||||
| margin-top: 1% | |||||
| max-height: 93% | |||||
| overflow-y: auto | |||||
| width: 75% | |||||
| background-color: #D82D48 | |||||
| .search-group | |||||
| @include vendor(transition, all 0.6s ease-out) | |||||
| span | |||||
| padding-left: 3px | |||||
| background-color: $lightGray | |||||
| padding: 1% | |||||
| margin-bottom: 2% | |||||
| width: 97% | |||||
| &:hover | |||||
| @extend .t3 | |||||
| >div | |||||
| margin-bottom: 0.7% | |||||
| background-color: #F11437 | |||||
| >div | |||||
| display: inline-block | |||||
| width: 18% | |||||
| #search-groups | |||||
| margin-top: 1% | |||||
| max-height: 93% | |||||
| overflow-y: auto | |||||
| width: 75% | |||||
| >input[type=text] | |||||
| padding: 2px | |||||
| width: 60% | |||||
| .search-group | |||||
| @include vendor(transition, all 0.6s ease-out) | |||||
| >input[type=checkbox] | |||||
| margin-left: 2% | |||||
| background-color: $lightGray | |||||
| padding: 1% | |||||
| margin-bottom: 2% | |||||
| width: 97% | |||||
| &:checked + span | |||||
| @extend .t2 | |||||
| >div | |||||
| margin-bottom: 0.7% | |||||
| color: green | |||||
| font-weight: bold | |||||
| >div.name | |||||
| display: inline-block | |||||
| width: 18% | |||||
| span.regex | |||||
| font-size: 80% | |||||
| >input[type=text] | |||||
| display: inline-block | |||||
| padding: 2px | |||||
| width: 60% | |||||
| &#selected | |||||
| background-color: #CACACA | |||||
| >input[type=checkbox] | |||||
| margin-left: 2% | |||||
| &.partly-visible | |||||
| margin-top: 0% | |||||
| padding-bottom: 3% | |||||
| position: absolute | |||||
| width: 100% | |||||
| &:checked + span | |||||
| @extend .t2 | |||||
| #title | |||||
| position: absolute | |||||
| top: -1% | |||||
| left: 1% | |||||
| color: green | |||||
| font-weight: bold | |||||
| span | |||||
| font-size: 50% | |||||
| span.regex | |||||
| font-size: 80% | |||||
| form#search-bar | |||||
| padding-top: 3% | |||||
| margin-left: auto | |||||
| margin-right: auto | |||||
| width: 60% | |||||
| &#selected | |||||
| background-color: #CACACA | |||||
| div#results | div#results | ||||
| margin: 3% auto 0 auto | margin: 3% auto 0 auto | ||||
| @@ -13,12 +13,12 @@ | |||||
| {{ assets.tag("index.css") }} | {{ assets.tag("index.css") }} | ||||
| <script> | <script> | ||||
| TYPEAHEAD_LANGUAGES = {{ typeahead_languages | safe }}; | |||||
| AUTOCOMPLETE_LANGUAGES = {{ autocomplete_languages | safe }}; | |||||
| </script> | </script> | ||||
| = endblock | = endblock | ||||
| = block body | = block body | ||||
| <div id="search-field" class="partly-visible"> | |||||
| <div id="search-field"> | |||||
| <a id="title" href="/"> | <a id="title" href="/"> | ||||
| <div id="title"> | <div id="title"> | ||||
| <span id="title-bit">bit</span | <span id="title-bit">bit</span | ||||
| @@ -83,9 +83,10 @@ | |||||
| <div id="search-groups"> | <div id="search-groups"> | ||||
| <div class="search-group" id="selected"> | <div class="search-group" id="selected"> | ||||
| <div id="language"> | <div id="language"> | ||||
| <div>language</div> | |||||
| <input class="language" name="language" type="text"> | |||||
| <input type="checkbox" name="regex"><span class="regex">Regex</span> | |||||
| <div class="name">language</div> | |||||
| <input id="autocomplete" class="language" name="language" type="text"> | |||||
| <input type="checkbox" name="regex"> | |||||
| <span class="regex">Regex</span> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| @@ -94,7 +95,6 @@ | |||||
| <button onclick="assembleQuery()"> | <button onclick="assembleQuery()"> | ||||
| Assemble | Assemble | ||||
| </button> | </button> | ||||
| </div> | </div> | ||||
| </form> | </form> | ||||
| </div> | </div> | ||||