1/** 2 * Requires Jquery UI AutoComplete 3 */ 4 $(document).ready(function() { 5 var QualAndCourseheadings = { 6 'Programme': 'Qualifications', 7 'Course': 'Courses' 8 }; 9 10 jQuery.widget('custom.typeaheadsearch', jQuery.ui.autocomplete, { 11 _init: function(){ 12 this._setOptions({ 13 appendTo: '#result-typeahead', 14 position: { 15 my: 'left+24 top+24', 16 at: 'left top', 17 of: '#result-typeahead' 18 }, 19 autoFocus: true 20 }); 21 }, 22 _create: function () { 23 this._super(); 24 this.widget().menu('option', 'items', '> :not(.ui-autocomplete-category)'); 25 this._on(this.element, { 26 input: function(event) { 27 this.search(); 28 }, 29 propertychange: function(event) { 30 this.search(); 31 }, 32 focus: function(event) { 33 this.search(); 34 }, 35 paste: function(event) { 36 this.search(); 37 } 38 }); 39 }, 40 _renderMenu: function (ul, items) { 41 var self = this; 42 currenttype = ""; 43 $.each(items, function(index, item) { 44 if (item.ClassName != currenttype) { 45 var header = $('<li class="ui-autocomplete-category">' 46 + '<div>' 47 + '<h3 class="tas-heading"></h3>' 48 + '</div>' 49 + '</li>'); 50 var heading = item.ClassName; 51 if(typeof QualAndCourseheadings[item.ClassName] != 'undefined') { 52 heading = QualAndCourseheadings[item.ClassName]; 53 } 54 header.find('.tas-heading').text(heading); 55 ul.append(header); 56 currenttype = item.ClassName; 57 } 58 var li = self._renderItemData(ul, item); 59 if ( typeof item.ClassName != "undefined") { 60 li.attr( "aria-label", item.ClassName + " : " + item.Code + " " + item.Title ); 61 li.attr( "aria-value", item.Code + " " + item.Title ); 62 } 63 }); 64 }, 65 _renderItem: function (ul, item) { 66 var label = item.Code + " " + item.Title; 67 return $('<li>') 68 // .data( 'ui-autocomplete-item', item ) // Not working 69 .append('<a href="' + item.Link + '" class="item-type-' + item.ClassName + 70 '" title="' + label + '" alt="' + label 71 + '" onclick="$(\'input[name=TypeAheadInput]\').val(\'\');"' 72 + 'data-gaevent-category="' + item.ClassName + '" data-gaevent-action="TypeAheadSearch" ' 73 + 'data-gaevent-label="' + item.Code + '">' 74 + '<div>' 75 + '<span>' + label + '</span>' 76 + '</div>' 77 + '</a>') 78 .appendTo(ul); 79 }, 80 _resizeMenu: function(){ 81 var wdth = this.element[0].offsetWidth; 82 wdth = wdth - 48; 83 this.menu.element.outerWidth(wdth); 84 } 85 }); 86 87 var TypeAheadSearch = { 88 path: $('input[name=TypeAheadInput]').attr('data-src'), 89 selector: 'input[name=TypeAheadInput]', 90 noResultSelector: '#no-result-typeahead', 91 resultSelector: '#result-typeahead', 92 instance: null, 93 ignoreWords: ['and', 'the', 'in', 'of'], 94 items: [], 95 minLength: 3, 96 init: function () { 97 // make sure the "no results" div doesn't show up on load 98 $(TypeAheadSearch.noResultSelector).hide(); 99 100 // remove the "no results" div when the character number goes below the minimum length 101 $(TypeAheadSearch.selector).on('input propertychange', function(){ 102 var val = $(this).val(); 103 if (val.length < TypeAheadSearch.minLength) { 104 $(TypeAheadSearch.noResultSelector).hide(); 105 } 106 }); 107 108 // remove the "no results" div when the INPUT has focus 109 $(TypeAheadSearch.selector).on('blur', function(){ 110 $(TypeAheadSearch.noResultSelector).hide(); 111 }); 112 113 TypeAheadSearch.load(function(){ 114 TypeAheadSearch.instance = jQuery(TypeAheadSearch.selector).typeaheadsearch({ 115 source: function (request, response) { 116 var searchText = TypeAheadSearch.cleanQuery(request.term); 117 var result = []; 118 var querys = searchText.split(' '); 119 if ($.trim(searchText) != '' && querys.length > 0) { 120 jQuery.each(TypeAheadSearch.items, function (index, item) { 121 var matched = true; 122 querys.forEach(function(query) { 123 matched = matched && (new RegExp(query, "i")).test(item.SearchText); 124 }); 125 if (matched) { 126 result.push(item); 127 } 128 }); 129 } 130 if (result.length === 0) { 131 $(TypeAheadSearch.noResultSelector).show(); 132 if (typeof ga_send_event === "function") { 133 ga_send_event({ 134 eventCategory: $('input[name=TypeAheadPageType]').val(), 135 eventAction: "TypeAheadSorry", 136 eventLabel: $('input[name=TypeAheadInput]').val() 137 }); 138 } 139 } 140 else { 141 $(TypeAheadSearch.resultSelector).show(); 142 } 143 response(result); 144 }, 145 minLength: TypeAheadSearch.minLength, 146 select: function (event, ui) { 147 event.preventDefault(); 148 var item = $(TypeAheadSearch.resultSelector + ' .ui-menu-item a.ui-state-active'); 149 if (typeof ga_send_event === "function") { 150 ga_send_event({ 151 eventCategory: $('input[name=TypeAheadPageType]').val(), 152 eventAction: item.attr("data-gaevent-action"), 153 eventLabel: item.attr("data-gaevent-label") 154 }); 155 } 156 $('input[name=TypeAheadInput]').val(item.text()); 157 window.location = item.attr('href'); 158 $(TypeAheadSearch.resultSelector).hide(); 159 return false;
160 }, 161 close: function(event,ui){ 162 $(TypeAheadSearch.noResultSelector).hide(); 163 $(TypeAheadSearch.resultSelector).hide(); 164 } 165 }).autocomplete("instance"); 166 }); 167 168 }, 169 load: function (callback) { 170 $('input[name=TypeAheadInput]').val(''); 171 $.getJSON(TypeAheadSearch.path, function (data) { 172 TypeAheadSearch.items = data; 173 if (typeof callback != "undefined") { 174 callback(); 175 } 176 }); 177 }, 178 cleanQuery: function (query) { 179 return (query.replace(new RegExp("^" + TypeAheadSearch.ignoreWords.join("\\s|^") + "\\s", "gi"), ' ') 180 .replace(new RegExp("\\s" + TypeAheadSearch.ignoreWords.join("$|\\s") + "$", "gi"), ' ') 181 .replace(new RegExp("\\s" + TypeAheadSearch.ignoreWords.join("\\s|\\s") + "\\s", "gi"), ' ')).trim(); 182 } 183 }; 184 TypeAheadSearch.init(); 185});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.