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> | ||||