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(){ | |||
| $(".search-group input#date-last-modified").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. | |||
| */ | |||
| (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(){ | |||
| $("div#sidebar input[type=checkbox]").prop("checked", false); | |||
| searchGroups.children("#selected").removeAttr("id"); | |||
| 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); | |||
| }); | |||
| // 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(){ | |||
| var currentGroup = $("div.search-group#selected"); | |||
| @@ -96,7 +86,7 @@ var searchGroups = $("div#search-groups"); | |||
| function createSearchGroupInput(fieldId){ | |||
| return [ | |||
| "<div id='" + fieldId + "'>", | |||
| "<div>" + fieldId.replace(/-/g, " ") + "</div>", | |||
| "<div class='name'>" + fieldId.replace(/-/g, " ") + "</div>", | |||
| "<input class='" + fieldId + "' name='" + fieldId + "'type='text'>", | |||
| "<input type='checkbox' name='regex'><span>Regex</span>", | |||
| "</div>" | |||
| @@ -111,28 +101,35 @@ function assembleQuery(){ | |||
| var groupQueries = []; | |||
| 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 ")); | |||
| } | |||
| // console.log(groupQueries.join(" OR ")); | |||
| console.log(groupQueries.join(" OR ")); | |||
| } | |||
| /* | |||
| * 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, "\\\""); | |||
| 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(){ | |||
| @@ -1,42 +1,24 @@ | |||
| /* | |||
| Stylesheet for index.html. | |||
| Stylesheet for `index.html`. | |||
| */ | |||
| @import mixins | |||
| @import variables | |||
| $minSearchFieldsWidth: 490px | |||
| .ui-datepicker | |||
| 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 | |||
| @extend .t2 | |||
| @@ -70,8 +52,6 @@ div#search-field | |||
| color: $baseColor2 | |||
| font-style: italic | |||
| $minSearchFieldsWidth: 490px | |||
| form#search-bar | |||
| min-width: $minSearchFieldsWidth | |||
| @@ -114,178 +94,180 @@ div#search-field | |||
| width: 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% | |||
| >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 | |||
| 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% | |||
| 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 | |||
| margin: 3% auto 0 auto | |||
| @@ -13,12 +13,12 @@ | |||
| {{ assets.tag("index.css") }} | |||
| <script> | |||
| TYPEAHEAD_LANGUAGES = {{ typeahead_languages | safe }}; | |||
| AUTOCOMPLETE_LANGUAGES = {{ autocomplete_languages | safe }}; | |||
| </script> | |||
| = endblock | |||
| = block body | |||
| <div id="search-field" class="partly-visible"> | |||
| <div id="search-field"> | |||
| <a id="title" href="/"> | |||
| <div id="title"> | |||
| <span id="title-bit">bit</span | |||
| @@ -83,9 +83,10 @@ | |||
| <div id="search-groups"> | |||
| <div class="search-group" id="selected"> | |||
| <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> | |||
| @@ -94,7 +95,6 @@ | |||
| <button onclick="assembleQuery()"> | |||
| Assemble | |||
| </button> | |||
| </div> | |||
| </form> | |||
| </div> | |||