From 8ac132084891fe12ebb53627ea2a44ae600066a7 Mon Sep 17 00:00:00 2001 From: Pusher Robot Date: Tue, 30 Jun 2015 23:44:14 -0400 Subject: [PATCH] Updated component to version 2.0.0 --- RELEASE-NOTES.md | 27 ++- composer.json | 2 +- index.js | 428 +++++++++++++++++++++++++++++++++-------------- package.js | 2 +- package.json | 12 +- search.css | 151 +++++++++++------ search.js | 428 +++++++++++++++++++++++++++++++++-------------- search.min.css | 6 +- search.min.js | 6 +- 9 files changed, 749 insertions(+), 313 deletions(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 18ea5e1..1220532 100755 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,3 +1,27 @@ +### Version 2.0.0 - June 30, 2015 + +- **Dropdown** - Added remote API integration with dropdown, to allow `search selection` to query against a remote dataset. +- **Dropdowns** - Added ability to add custom choices to all search selection dropdowns (multi/single) using `allowAdditions: true` setting. Search now displays error messages on no results in all cases. +- **Dropdown** - `fullTextSearch: true` now uses fuzzy search (same as `ui search`) +- **Search** - Cache can now be cleared using `$('.search').search('clear cache')` +- **Search** - Search now operates off a unique id generated by result position to retrieve results. For example category #1's first result is 'A1' . Previously result titles were used as their "id", which could cause issues with duplicate titles, or results that do not contain a title. +- **Search** - Search will now automatically add class `category` when using `type: category`. +- **Search** - Search will now generate `results` container if one is not present on init +- **Search** - Search now uses `em` for resizes, making sure it will resize with the surrounding content +- **Search** - Search `prompt` now has focus styles defined if not using `ui input` +- **Dropdown** - Fix issue with search dropdown refocusing on self the first time after "tabbing" away in Chrome +- **Dropdown** - Fixed issue with `search selection` not changing text when reselecting same value from list +- **Dropdown** - Fixed `search dropdown` submitting parent form when enter shortcut pressed +- **Search** - Search will no longer incorrectly produce an error when API settings are passed through metadata +- **Search** - Fixed `category search` not applying active styles correctly to category names +- **Search** - Fixed `onSelect` not returning the correct value when using `type: category` +- **Search** - Fixed `onSelect` returning the first term that matches the beginining of the selected value not the exact value. +- **Search** - Fix `loading search` with an `icon button` causing double loaders. +- **Search** - `searchFields` setting now correctly replaces default fields instead of adding the user fields to defaults +- **Search** - Calls to `set value` or `query` now obey `minCharacterLength` +- **Search** - Search API calls now use the same level debug settings as search +- **Search** - Slightly adjusted search result theme for clarity + ### Version 1.11.5 - March 23, 2015 - **Dropdown** - Fix bug where element will not blur on tab key when search selection and no selection made @@ -9,6 +33,7 @@ ### Version 1.11.0 - March 3, 2015 - **Dropdown** - Fixes issue where dropdown would not open after restoring previus value on failed `search dropdown` search +- **Dropdown** - Fixes issue where dropdown would not open after restoring previous value on failed `search dropdown` search - **Search** - Fix special characters not searching correctly with local search - **Search** - Fix a bug with `onSelect` returning `null` when `minCharacters: 0` - **Search** - Fix a bug with `onSelect returning `null` when results retrieved from cached API query @@ -29,7 +54,7 @@ ### Version 1.8.0 - January 23, 2015 -- **Search** - Search `onSelect` now recieves JSON object matching currently selected element, you can now programmatically retrieve result JSON using `.search('get result', 'query')` or `.search('get results')`. `get result` will default to current value unless specified as first parameter. +- **Search** - Search `onSelect` now receives JSON object matching currently selected element, you can now programmatically retrieve result JSON using `.search('get result', 'query')` or `.search('get results')`. `get result` will default to current value unless specified as first parameter. - **Search** - Greatly reduced search delay from `300ms` to `100ms`. Previous request will automatically abort `xhr` when new request made - **Search** - Search `onSelect` and `onResultsAdd` can now cancel default actions by returning `false`. - **Dropdown** - Dropdown no longer will not show menu when no `item` are present in menu. Dropdown will now only filter results for `ui search dropdown` #1632 **Thanks PSyton**. diff --git a/composer.json b/composer.json index 5109cc7..9a1749f 100755 --- a/composer.json +++ b/composer.json @@ -15,5 +15,5 @@ "framework" ], "license": "MIT", - "version": "1.12.3" + "version": "2.0.0" } \ No newline at end of file diff --git a/index.js b/index.js index c9e1ce4..af219ee 100755 --- a/index.js +++ b/index.js @@ -1,9 +1,9 @@ /*! - * # Semantic UI 1.12.3 - Search + * # Semantic UI 2.0.0 - Search * http://github.com/semantic-org/semantic-ui/ * * - * Copyright 2014 Contributors + * Copyright 2015 Contributors * Released under the MIT license * http://opensource.org/licenses/MIT * @@ -30,7 +30,9 @@ module.exports = function(parameters) { $(this) .each(function() { var - settings = $.extend(true, {}, _module.exports.settings, parameters), + settings = ( $.isPlainObject(parameters) ) + ? $.extend(true, {}, _module.exports.settings, parameters) + : $.extend({}, _module.exports.settings), className = settings.className, metadata = settings.metadata, @@ -54,37 +56,15 @@ module.exports = function(parameters) { module ; + module = { initialize: function() { module.verbose('Initializing module'); - var - prompt = $prompt[0], - inputEvent = (prompt !== undefined && prompt.oninput !== undefined) - ? 'input' - : (prompt !== undefined && prompt.onpropertychange !== undefined) - ? 'propertychange' - : 'keyup' - ; - if(settings.automatic) { - $prompt - .on(inputEvent + eventNamespace, module.throttle) - .attr('autocomplete', 'off') - ; - } - $prompt - .on('focus' + eventNamespace, module.event.focus) - .on('blur' + eventNamespace, module.event.blur) - .on('keydown' + eventNamespace, module.handleKeyboard) - ; - $searchButton - .on('click' + eventNamespace, module.query) - ; - $results - .on('mousedown' + eventNamespace, module.event.result.mousedown) - .on('mouseup' + eventNamespace, module.event.result.mouseup) - .on('click' + eventNamespace, selector.result, module.event.result.click) - ; + module.determine.searchFields(); + module.bind.events(); + module.set.type(); + module.create.results(); module.instantiate(); }, instantiate: function() { @@ -97,24 +77,56 @@ module.exports = function(parameters) { destroy: function() { module.verbose('Destroying instance'); $module - .removeData(moduleNamespace) - ; - $prompt - .off(eventNamespace) - ; - $searchButton - .off(eventNamespace) - ; - $results .off(eventNamespace) + .removeData(moduleNamespace) ; }, + + bind: { + events: function() { + module.verbose('Binding events to search'); + if(settings.automatic) { + $module + .on(module.get.inputEvent() + eventNamespace, selector.prompt, module.event.input) + ; + $prompt + .attr('autocomplete', 'off') + ; + } + $module + // prompt + .on('focus' + eventNamespace, selector.prompt, module.event.focus) + .on('blur' + eventNamespace, selector.prompt, module.event.blur) + .on('keydown' + eventNamespace, selector.prompt, module.handleKeyboard) + // search button + .on('click' + eventNamespace, selector.searchButton, module.query) + // results + .on('mousedown' + eventNamespace, selector.results, module.event.result.mousedown) + .on('mouseup' + eventNamespace, selector.results, module.event.result.mouseup) + .on('click' + eventNamespace, selector.result, module.event.result.click) + ; + } + }, + + determine: { + searchFields: function() { + // this makes sure $.extend does not add specified search fields to default fields + // this is the only setting which should not extend defaults + if(parameters && parameters.searchFields !== undefined) { + settings.searchFields = parameters.searchFields; + } + } + }, + event: { + input: function() { + clearTimeout(module.timer); + module.timer = setTimeout(module.query, settings.searchDelay); + }, focus: function() { module.set.focus(); - clearTimeout(module.timer); - module.throttle(); if( module.has.minimumCharacters() ) { + module.query(); module.showResults(); } }, @@ -144,11 +156,12 @@ module.exports = function(parameters) { href = $link.attr('href') || false, target = $link.attr('target') || false, title = $title.html(), - name = ($title.length > 0) + // title is used for result lookup + value = ($title.length > 0) ? $title.text() : false, results = module.get.results(), - result = module.get.result(name, results), + result = $result.data(metadata.result) || module.get.result(value, results), returnedValue ; if( $.isFunction(settings.onSelect) ) { @@ -158,8 +171,8 @@ module.exports = function(parameters) { } } module.hideResults(); - if(name) { - module.set.value(name); + if(value) { + module.set.value(value); } if(href) { module.verbose('Opening search link found in result', $link); @@ -259,9 +272,11 @@ module.exports = function(parameters) { api: function() { var apiSettings = { + debug : settings.debug, on : false, + cache : 'local', action : 'search', - onFailure : module.error + onError : module.error }, searchHTML ; @@ -292,6 +307,17 @@ module.exports = function(parameters) { }, get: { + inputEvent: function() { + var + prompt = $prompt[0], + inputEvent = (prompt !== undefined && prompt.oninput !== undefined) + ? 'input' + : (prompt !== undefined && prompt.onpropertychange !== undefined) + ? 'propertychange' + : 'keyup' + ; + return inputEvent; + }, value: function() { return $prompt.val(); }, @@ -303,26 +329,34 @@ module.exports = function(parameters) { }, result: function(value, results) { var - result = false + lookupFields = ['title', 'id'], + result = false + ; + value = (value !== undefined) + ? value + : module.get.value() + ; + results = (results !== undefined) + ? results + : module.get.results() ; - value = value || module.get.value(); - results = results || module.get.results(); if(settings.type === 'category') { module.debug('Finding result that matches', value); $.each(results, function(index, category) { if($.isArray(category.results)) { - result = module.search.object(value, category.results)[0]; - if(result && result.length > 0) { - return true; + result = module.search.object(value, category.results, lookupFields)[0]; + // dont continue searching if a result is found + if(result) { + return false; } } }); } else { module.debug('Finding result in results object', value); - result = module.search.object(value, results)[0]; + result = module.search.object(value, results, lookupFields)[0]; } - return result; + return result || false; }, }, @@ -335,8 +369,15 @@ module.exports = function(parameters) { }, value: function(value) { module.verbose('Setting search input value', value); - $prompt.val(value); - module.query(); + $prompt + .val(value) + ; + }, + type: function(type) { + type = type || settings.type; + if(settings.type == 'category') { + $module.addClass(settings.type); + } }, buttonPressed: function() { $searchButton.addClass(className.pressed); @@ -360,55 +401,52 @@ module.exports = function(parameters) { searchTerm = module.get.value(), cache = module.read.cache(searchTerm) ; - if(cache) { - module.debug('Reading result for ' + searchTerm + ' from cache'); - module.save.results(cache.results); - module.addResults(cache.html); - } - else { - module.debug('Querying for ' + searchTerm); - if($.isPlainObject(settings.source) || $.isArray(settings.source)) { - module.search.local(searchTerm); + if( module.has.minimumCharacters() ) { + if(cache) { + module.debug('Reading result from cache', searchTerm); + module.save.results(cache.results); + module.addResults(cache.html); + module.inject.id(cache.results); } - else if( module.can.useAPI() ) { - if(settings.apiSettings) { - module.debug('Searching with specified API settings', settings.apiSettings); - module.search.remote(searchTerm); + else { + module.debug('Querying for', searchTerm); + if($.isPlainObject(settings.source) || $.isArray(settings.source)) { + module.search.local(searchTerm); } - else if($.api.settings.api.search !== undefined) { - module.debug('Searching with default search API endpoint'); + else if( module.can.useAPI() ) { module.search.remote(searchTerm); } else { - module.error(error.noEndpoint); + module.error(error.source); } + settings.onSearchQuery.call(element, searchTerm); } - else { - module.error(error.source); - } - settings.onSearchQuery.call(element, searchTerm); + } + else { + module.hideResults(); } }, search: { local: function(searchTerm) { var - searchResults = module.search.object(searchTerm, settings.content), + results = module.search.object(searchTerm, settings.content), searchHTML ; module.set.loading(); - module.save.results(searchResults); - module.debug('Returned local search results', searchResults); + module.save.results(results); + module.debug('Returned local search results', results); searchHTML = module.generateResults({ - results: searchResults + results: results }); module.remove.loading(); + module.addResults(searchHTML); + module.inject.id(results); module.write.cache(searchTerm, { html : searchHTML, - results : searchResults + results : results }); - module.addResults(searchHTML); }, remote: function(searchTerm) { var @@ -416,6 +454,9 @@ module.exports = function(parameters) { onSuccess : function(response) { module.parse.response.call(element, response, searchTerm); }, + onFailure: function() { + module.displayMessage(error.serverError); + }, urlData: { query: searchTerm } @@ -432,43 +473,60 @@ module.exports = function(parameters) { .api('query') ; }, - object: function(searchTerm, source) { + object: function(searchTerm, source, searchFields) { var - results = [], - fullTextResults = [], - searchFields = $.isArray(settings.searchFields) - ? settings.searchFields - : [settings.searchFields], - searchExp = searchTerm.replace(regExp.escape, '\\$&'), - searchRegExp = new RegExp(regExp.exact + searchExp, 'i') - ; + results = [], + fuzzyResults = [], + searchExp = searchTerm.toString().replace(regExp.escape, '\\$&'), + matchRegExp = new RegExp(regExp.beginsWith + searchExp, 'i'), + // avoid duplicates when pushing results + addResult = function(array, result) { + var + notResult = ($.inArray(result, results) == -1), + notFuzzyResult = ($.inArray(result, fuzzyResults) == -1) + ; + if(notResult && notFuzzyResult) { + array.push(result); + } + } + ; source = source || settings.source; + searchFields = (searchFields !== undefined) + ? searchFields + : settings.searchFields + ; + + // search fields should be array to loop correctly + if(!$.isArray(searchFields)) { + searchFields = [searchFields]; + } - // exit conditions on no source - if(source === undefined) { + // exit conditions if no source + if(source === undefined || source === false) { module.error(error.source); return []; } - // iterate through search fields in array order + // iterate through search fields looking for matches $.each(searchFields, function(index, field) { $.each(source, function(label, content) { var - fieldExists = (typeof content[field] == 'string'), - notAlreadyResult = ($.inArray(content, results) == -1 && $.inArray(content, fullTextResults) == -1) + fieldExists = (typeof content[field] == 'string') ; - if(fieldExists && notAlreadyResult) { - if( content[field].match(searchRegExp) ) { - results.push(content); + if(fieldExists) { + if( content[field].search(matchRegExp) !== -1) { + // content starts with value (first in results) + addResult(results, content); } else if(settings.searchFullText && module.fuzzySearch(searchTerm, content[field]) ) { - fullTextResults.push(content); + // content fuzzy matches (last in results) + addResult(fuzzyResults, content); } } }); }); - return $.merge(results, fullTextResults); + return $.merge(results, fuzzyResults); } }, @@ -477,6 +535,9 @@ module.exports = function(parameters) { termLength = term.length, queryLength = query.length ; + if(typeof query !== 'string') { + return false; + } query = query.toLowerCase(); term = term.toLowerCase(); if(queryLength > termLength) { @@ -507,27 +568,18 @@ module.exports = function(parameters) { module.verbose('Parsing server response', response); if(response !== undefined) { if(searchTerm !== undefined && response.results !== undefined) { + module.addResults(searchHTML); + module.inject.id(response.results); module.write.cache(searchTerm, { html : searchHTML, results : response.results }); module.save.results(response.results); - module.addResults(searchHTML); } } } }, - throttle: function() { - clearTimeout(module.timer); - if(module.has.minimumCharacters()) { - module.timer = setTimeout(module.query, settings.searchDelay); - } - else { - module.hideResults(); - } - }, - cancel: { query: function() { if( module.can.useAPI() ) { @@ -546,6 +598,23 @@ module.exports = function(parameters) { } }, + clear: { + cache: function(value) { + var + cache = $module.data(metadata.cache) + ; + if(!value) { + module.debug('Clearing cache', value); + $module.removeData(metadata.cache); + } + else if(value && cache && cache[value]) { + module.debug('Removing value from cache', value); + delete cache[value]; + $module.data(metadata.cache, cache); + } + } + }, + read: { cache: function(name) { var @@ -562,6 +631,94 @@ module.exports = function(parameters) { } }, + create: { + id: function(resultIndex, categoryIndex) { + var + resultID = (resultIndex + 1), // not zero indexed + categoryID = (categoryIndex + 1), + firstCharCode, + letterID, + id + ; + if(categoryIndex !== undefined) { + // start char code for "A" + letterID = String.fromCharCode(97 + categoryIndex); + id = letterID + resultID; + module.verbose('Creating category result id', id); + } + else { + id = resultID; + module.verbose('Creating result id', id); + } + return id; + }, + results: function() { + if($results.length === 0) { + $results = $('
') + .addClass(className.results) + .appendTo($module) + ; + } + } + }, + + inject: { + result: function(result, resultIndex, categoryIndex) { + module.verbose('Injecting result into results'); + var + $selectedResult = (categoryIndex !== undefined) + ? $results + .children().eq(categoryIndex) + .children(selector.result).eq(resultIndex) + : $results + .children(selector.result).eq(resultIndex) + ; + module.verbose('Injecting results metadata', $selectedResult); + $selectedResult + .data(metadata.result, result) + ; + }, + id: function(results) { + module.debug('Injecting unique ids into results'); + var + // since results may be object, we must use counters + categoryIndex = 0, + resultIndex = 0 + ; + if(settings.type === 'category') { + // iterate through each category result + $.each(results, function(index, category) { + resultIndex = 0; + $.each(category.results, function(index, value) { + var + result = category.results[index] + ; + if(result.id === undefined) { + result.id = module.create.id(resultIndex, categoryIndex); + } + module.inject.result(result, resultIndex, categoryIndex); + resultIndex++; + }); + categoryIndex++; + }); + } + else { + // top level + $.each(results, function(index, value) { + var + result = results[index] + ; + if(result.id === undefined) { + result.id = module.create.id(resultIndex); + } + module.inject.result(result, resultIndex); + resultIndex++; + }); + } + return results; + } + }, + save: { results: function(results) { module.verbose('Saving current search results to metadata', results); @@ -606,6 +763,8 @@ module.exports = function(parameters) { $results .transition({ animation : settings.transition + ' in', + debug : settings.debug, + verbose : settings.verbose, duration : settings.duration, queue : true }) @@ -628,6 +787,8 @@ module.exports = function(parameters) { $results .transition({ animation : settings.transition + ' out', + debug : settings.debug, + verbose : settings.verbose, duration : settings.duration, queue : true }) @@ -752,7 +913,7 @@ module.exports = function(parameters) { }); } clearTimeout(module.performance.timer); - module.performance.timer = setTimeout(module.performance.display, 100); + module.performance.timer = setTimeout(module.performance.display, 500); }, display: function() { var @@ -864,36 +1025,55 @@ module.exports = function(parameters) { module.exports.settings = { - name : 'Search Module', + name : 'Search', namespace : 'search', debug : false, - verbose : true, + verbose : false, performance : true, type : 'standard', + // template to use (specified in settings.templates) + minCharacters : 1, + // minimum characters required to search - // api config apiSettings : false, + // API config source : false, + // object to search + searchFields : [ 'title', 'description' ], + // fields to search + searchFullText : true, + // whether to include fuzzy results in local search + + automatic : true, + // whether to add events to prompt automatically - automatic : 'true', hideDelay : 0, - searchDelay : 100, + // delay before hiding menu after blur + + searchDelay : 200, + // delay before searching + maxResults : 7, + // maximum results returned from local + cache : true, + // whether to store lookups in local cache + // transition settings transition : 'scale', - duration : 300, + duration : 200, easing : 'easeOutExpo', + // callbacks onSelect : false, onResultsAdd : false, @@ -908,6 +1088,7 @@ module.exports.settings = { empty : 'empty', focus : 'focus', loading : 'loading', + results : 'results', pressed : 'down' }, @@ -917,19 +1098,20 @@ module.exports.settings = { logging : 'Error in debug logging, exiting.', noEndpoint : 'No search endpoint was specified', noTemplate : 'A valid template name was not specified.', - serverError : 'There was an issue with querying the server.', + serverError : 'There was an issue querying the server.', maxResults : 'Results must be an array to use maxResults setting', method : 'The method you called is not defined.' }, metadata: { cache : 'cache', - results : 'results' + results : 'results', + result : 'result' }, regExp: { - escape : /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, - exact : '(?:\s|^)' + escape : /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, + beginsWith : '(?:\s|^)' }, selector : { diff --git a/package.js b/package.js index 55cf55f..f7d0a40 100755 --- a/package.js +++ b/package.js @@ -2,7 +2,7 @@ Package.describe({ name : 'semantic:ui-search', summary : 'Semantic UI - Search: Single component release', - version : '1.12.3', + version : '2.0.0', git : 'git://github.com/Semantic-Org/UI-Search.git', }); diff --git a/package.json b/package.json index 0c690b9..5d96785 100755 --- a/package.json +++ b/package.json @@ -1,17 +1,17 @@ { - "name": "semantic-ui-search", - "version": "1.12.3", - "title": "Semantic UI - Search", - "description": "Single component release of search", + "name": "semantic", + "version": "1.0.0", + "title": "Semantic UI", + "description": "Semantic empowers designers and developers by creating a shared vocabulary for UI.", "homepage": "http://www.semantic-ui.com", "author": "Jack Lukic ", "license": "MIT", "repository": { "type": "git", - "url": "https://github.com/Semantic-Org/UI-Search.git" + "url": "git://github.com/Semantic-Org/Semantic-UI.git" }, "bugs": { "url": "https://github.com/Semantic-Org/Semantic-UI/issues" }, "devDependencies": {} -} \ No newline at end of file +} diff --git a/search.css b/search.css index df3c27c..838a30e 100755 --- a/search.css +++ b/search.css @@ -1,9 +1,9 @@ /*! - * # Semantic UI 1.12.3 - Search + * # Semantic UI 2.0.0 - Search * http://github.com/semantic-org/semantic-ui/ * * - * Copyright 2014 Contributors + * Copyright 2015 Contributors * Released under the MIT license * http://opensource.org/licenses/MIT * @@ -25,15 +25,15 @@ text-shadow: none; font-style: normal; font-weight: normal; - line-height: 1.2; - padding: 0.68571em 1em; + line-height: 1.2142em; + padding: 0.67861429em 1em; font-size: 1em; background: #ffffff; - border: 1px solid rgba(39, 41, 43, 0.15); - color: rgba(0, 0, 0, 0.8); + border: 1px solid rgba(34, 36, 38, 0.15); + color: rgba(0, 0, 0, 0.87); box-shadow: 0em 0em 0em 0em transparent inset; - -webkit-transition: background-color 0.2s ease, color 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease; - transition: background-color 0.2s ease, color 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease; + -webkit-transition: background-color 0.1s ease, color 0.1s ease, box-shadow 0.1s ease, border-color 0.1s ease; + transition: background-color 0.1s ease, color 0.1s ease, box-shadow 0.1s ease, border-color 0.1s ease; } .ui.search .prompt { border-radius: 500rem; @@ -56,13 +56,23 @@ position: absolute; top: 100%; left: 0%; + -webkit-transform-origin: center top; + -ms-transform-origin: center top; + transform-origin: center top; background: #ffffff; margin-top: 0.5em; - width: 16em; - border-radius: 0.25em; - box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.2); + width: 18em; + border-radius: 0.28571429rem; + box-shadow: 0px 2px 4px 0px rgba(34, 36, 38, 0.12), 0px 2px 10px 0px rgba(34, 36, 38, 0.08); + border: 1px solid #d4d4d5; z-index: 998; } +.ui.search > .results > :first-child { + border-radius: 0.28571429rem 0.28571429rem 0em 0em; +} +.ui.search > .results > :last-child { + border-radius: 0em 0em 0.28571429rem 0.28571429rem; +} /*-------------- Result @@ -73,13 +83,13 @@ display: block; overflow: hidden; font-size: 1em; - padding: 0.5em 1em; - color: rgba(0, 0, 0, 0.8); + padding: 0.85714286em 1.14285714em; + color: rgba(0, 0, 0, 0.87); line-height: 1.33; - border-bottom: 1px solid rgba(39, 41, 43, 0.15); + border-bottom: 1px solid rgba(34, 36, 38, 0.1); } .ui.search > .results .result:last-child { - border-bottom: none; + border-bottom: none !important; } /* Image */ @@ -105,19 +115,20 @@ margin: 0em 6em 0em 0em; } .ui.search > .results .result .title { + margin: -0.14285em 0em 0em; font-family: 'Lato', 'Helvetica Neue', Arial, Helvetica, sans-serif; font-weight: bold; font-size: 1em; color: rgba(0, 0, 0, 0.85); } .ui.search > .results .result .description { - margin-top: 0em; - font-size: 0.9285em; + margin-top: 0; + font-size: 0.92857143em; color: rgba(0, 0, 0, 0.4); } .ui.search > .results .result .price { float: right; - color: #5bbd72; + color: #21ba45; } /*-------------- @@ -129,23 +140,23 @@ } .ui.search > .results > .message .header { font-family: 'Lato', 'Helvetica Neue', Arial, Helvetica, sans-serif; - font-size: 1.1428em; + font-size: 1rem; font-weight: bold; - color: rgba(0, 0, 0, 0.8); + color: rgba(0, 0, 0, 0.87); } .ui.search > .results > .message .description { margin-top: 0.25rem; font-size: 1em; - color: rgba(0, 0, 0, 0.8); + color: rgba(0, 0, 0, 0.87); } /* View All Results */ .ui.search > .results > .action { display: block; border-top: none; - background: #f0f0f0; - padding: 0.5em 1em; - color: rgba(0, 0, 0, 0.8); + background: #f3f4f5; + padding: 0.92857143em 1em; + color: rgba(0, 0, 0, 0.87); font-weight: bold; text-align: center; } @@ -156,35 +167,45 @@ *******************************/ +/*-------------------- + Focus +---------------------*/ + +.ui.search > .prompt:focus { + border-color: rgba(34, 36, 38, 0.35); + background: #ffffff; + color: rgba(0, 0, 0, 0.95); +} + /*-------------------- Loading ---------------------*/ -.ui.loading.search .input > .icon:before { +.ui.loading.search .input > i.icon:before { position: absolute; content: ''; top: 50%; left: 50%; - margin: -0.64285em 0em 0em -0.64285em; - width: 1.2857em; - height: 1.2857em; + margin: -0.64285714em 0em 0em -0.64285714em; + width: 1.28571429em; + height: 1.28571429em; border-radius: 500rem; border: 0.2em solid rgba(0, 0, 0, 0.1); } -.ui.loading.search .input > .icon:after { +.ui.loading.search .input > i.icon:after { position: absolute; content: ''; top: 50%; left: 50%; - margin: -0.64285em 0em 0em -0.64285em; - width: 1.2857em; - height: 1.2857em; + margin: -0.64285714em 0em 0em -0.64285714em; + width: 1.28571429em; + height: 1.28571429em; -webkit-animation: button-spin 0.6s linear; animation: button-spin 0.6s linear; -webkit-animation-iteration-count: infinite; animation-iteration-count: infinite; border-radius: 500rem; - border-color: #aaaaaa transparent transparent; + border-color: #767676 transparent transparent; border-style: solid; border-width: 0.2em; box-shadow: 0px 0px 0px 1px transparent; @@ -196,7 +217,7 @@ .ui.search > .results .result:hover, .ui.category.search > .results .category .result:hover { - background: #fafafa; + background: #f9fafb; } .ui.search .action:hover { background: #e0e0e0; @@ -206,18 +227,18 @@ Active ---------------*/ -.ui.search > .results .category.active { - background: #f0f0f0; +.ui.category.search > .results .category.active { + background: #f3f4f5; } -.ui.search > .results .category.active > .name { - color: rgba(0, 0, 0, 0.8); +.ui.category.search > .results .category.active > .name { + color: rgba(0, 0, 0, 0.87); } .ui.search > .results .result.active, .ui.category.search > .results .category .result.active { position: relative; - border-left-color: transparent; - background: #f0f0f0; - box-shadow: 3px 0px 3px 0px rgba(39, 41, 43, 0.15); + border-left-color: rgba(34, 36, 38, 0.1); + background: #f3f4f5; + box-shadow: none; } .ui.search > .results .result.active .title { color: rgba(0, 0, 0, 0.85); @@ -242,33 +263,44 @@ /* Category */ .ui.category.search > .results .category { - background: #f0f0f0; + background: #f3f4f5; box-shadow: none; - border-bottom: 1px solid rgba(39, 41, 43, 0.15); - -webkit-transition: background 0.2s ease, border-color 0.2s ease; - transition: background 0.2s ease, border-color 0.2s ease; + border-bottom: 1px solid rgba(34, 36, 38, 0.1); + -webkit-transition: background 0.1s ease, border-color 0.1s ease; + transition: background 0.1s ease, border-color 0.1s ease; } + +/* Last Category */ .ui.category.search > .results .category:last-child { border-bottom: none; } +/* First / Last */ +.ui.category.search > .results .category:first-child .name + .result { + border-radius: 0em 0.28571429rem 0em 0em; +} +.ui.category.search > .results .category:last-child .result:last-child { + border-radius: 0em 0em 0.28571429rem 0em; +} + /* Category Result */ .ui.category.search > .results .category .result { background: #ffffff; margin-left: 100px; - border-left: 1px solid rgba(39, 41, 43, 0.15); - border-bottom: 1px solid rgba(39, 41, 43, 0.15); - -webkit-transition: background 0.2s ease, border-color 0.2s ease; - transition: background 0.2s ease, border-color 0.2s ease; + border-left: 1px solid rgba(34, 36, 38, 0.15); + border-bottom: 1px solid rgba(34, 36, 38, 0.1); + -webkit-transition: background 0.1s ease, border-color 0.1s ease; + transition: background 0.1s ease, border-color 0.1s ease; + padding: 0.85714286em 1.14285714em; } -.ui.category.search > .results .category .result:last-child { +.ui.category.search > .results .category:last-child .result:last-child { border-bottom: none; } /* Category Result Name */ .ui.category.search > .results .category > .name { width: 100px; - background: #f0f0f0; + background: transparent; font-family: 'Lato', 'Helvetica Neue', Arial, Helvetica, sans-serif; font-size: 1em; float: 1em; @@ -309,11 +341,26 @@ Sizes ---------------*/ +.ui.mini.search { + font-size: 0.71428571em; +} +.ui.small.search { + font-size: 0.92857143em; +} .ui.search { font-size: 1em; } .ui.large.search { - font-size: 1.1em; + font-size: 1.14285714em; +} +.ui.big.search { + font-size: 1.28571429em; +} +.ui.huge.search { + font-size: 1.42857143em; +} +.ui.massive.search { + font-size: 1.71428571em; } diff --git a/search.js b/search.js index 506e291..fb07902 100755 --- a/search.js +++ b/search.js @@ -1,9 +1,9 @@ /*! - * # Semantic UI 1.12.3 - Search + * # Semantic UI 2.0.0 - Search * http://github.com/semantic-org/semantic-ui/ * * - * Copyright 2014 Contributors + * Copyright 2015 Contributors * Released under the MIT license * http://opensource.org/licenses/MIT * @@ -29,7 +29,9 @@ $.fn.search = function(parameters) { $(this) .each(function() { var - settings = $.extend(true, {}, $.fn.search.settings, parameters), + settings = ( $.isPlainObject(parameters) ) + ? $.extend(true, {}, $.fn.search.settings, parameters) + : $.extend({}, $.fn.search.settings), className = settings.className, metadata = settings.metadata, @@ -53,37 +55,15 @@ $.fn.search = function(parameters) { module ; + module = { initialize: function() { module.verbose('Initializing module'); - var - prompt = $prompt[0], - inputEvent = (prompt !== undefined && prompt.oninput !== undefined) - ? 'input' - : (prompt !== undefined && prompt.onpropertychange !== undefined) - ? 'propertychange' - : 'keyup' - ; - if(settings.automatic) { - $prompt - .on(inputEvent + eventNamespace, module.throttle) - .attr('autocomplete', 'off') - ; - } - $prompt - .on('focus' + eventNamespace, module.event.focus) - .on('blur' + eventNamespace, module.event.blur) - .on('keydown' + eventNamespace, module.handleKeyboard) - ; - $searchButton - .on('click' + eventNamespace, module.query) - ; - $results - .on('mousedown' + eventNamespace, module.event.result.mousedown) - .on('mouseup' + eventNamespace, module.event.result.mouseup) - .on('click' + eventNamespace, selector.result, module.event.result.click) - ; + module.determine.searchFields(); + module.bind.events(); + module.set.type(); + module.create.results(); module.instantiate(); }, instantiate: function() { @@ -96,24 +76,56 @@ $.fn.search = function(parameters) { destroy: function() { module.verbose('Destroying instance'); $module - .removeData(moduleNamespace) - ; - $prompt - .off(eventNamespace) - ; - $searchButton - .off(eventNamespace) - ; - $results .off(eventNamespace) + .removeData(moduleNamespace) ; }, + + bind: { + events: function() { + module.verbose('Binding events to search'); + if(settings.automatic) { + $module + .on(module.get.inputEvent() + eventNamespace, selector.prompt, module.event.input) + ; + $prompt + .attr('autocomplete', 'off') + ; + } + $module + // prompt + .on('focus' + eventNamespace, selector.prompt, module.event.focus) + .on('blur' + eventNamespace, selector.prompt, module.event.blur) + .on('keydown' + eventNamespace, selector.prompt, module.handleKeyboard) + // search button + .on('click' + eventNamespace, selector.searchButton, module.query) + // results + .on('mousedown' + eventNamespace, selector.results, module.event.result.mousedown) + .on('mouseup' + eventNamespace, selector.results, module.event.result.mouseup) + .on('click' + eventNamespace, selector.result, module.event.result.click) + ; + } + }, + + determine: { + searchFields: function() { + // this makes sure $.extend does not add specified search fields to default fields + // this is the only setting which should not extend defaults + if(parameters && parameters.searchFields !== undefined) { + settings.searchFields = parameters.searchFields; + } + } + }, + event: { + input: function() { + clearTimeout(module.timer); + module.timer = setTimeout(module.query, settings.searchDelay); + }, focus: function() { module.set.focus(); - clearTimeout(module.timer); - module.throttle(); if( module.has.minimumCharacters() ) { + module.query(); module.showResults(); } }, @@ -143,11 +155,12 @@ $.fn.search = function(parameters) { href = $link.attr('href') || false, target = $link.attr('target') || false, title = $title.html(), - name = ($title.length > 0) + // title is used for result lookup + value = ($title.length > 0) ? $title.text() : false, results = module.get.results(), - result = module.get.result(name, results), + result = $result.data(metadata.result) || module.get.result(value, results), returnedValue ; if( $.isFunction(settings.onSelect) ) { @@ -157,8 +170,8 @@ $.fn.search = function(parameters) { } } module.hideResults(); - if(name) { - module.set.value(name); + if(value) { + module.set.value(value); } if(href) { module.verbose('Opening search link found in result', $link); @@ -258,9 +271,11 @@ $.fn.search = function(parameters) { api: function() { var apiSettings = { + debug : settings.debug, on : false, + cache : 'local', action : 'search', - onFailure : module.error + onError : module.error }, searchHTML ; @@ -291,6 +306,17 @@ $.fn.search = function(parameters) { }, get: { + inputEvent: function() { + var + prompt = $prompt[0], + inputEvent = (prompt !== undefined && prompt.oninput !== undefined) + ? 'input' + : (prompt !== undefined && prompt.onpropertychange !== undefined) + ? 'propertychange' + : 'keyup' + ; + return inputEvent; + }, value: function() { return $prompt.val(); }, @@ -302,26 +328,34 @@ $.fn.search = function(parameters) { }, result: function(value, results) { var - result = false + lookupFields = ['title', 'id'], + result = false + ; + value = (value !== undefined) + ? value + : module.get.value() + ; + results = (results !== undefined) + ? results + : module.get.results() ; - value = value || module.get.value(); - results = results || module.get.results(); if(settings.type === 'category') { module.debug('Finding result that matches', value); $.each(results, function(index, category) { if($.isArray(category.results)) { - result = module.search.object(value, category.results)[0]; - if(result && result.length > 0) { - return true; + result = module.search.object(value, category.results, lookupFields)[0]; + // dont continue searching if a result is found + if(result) { + return false; } } }); } else { module.debug('Finding result in results object', value); - result = module.search.object(value, results)[0]; + result = module.search.object(value, results, lookupFields)[0]; } - return result; + return result || false; }, }, @@ -334,8 +368,15 @@ $.fn.search = function(parameters) { }, value: function(value) { module.verbose('Setting search input value', value); - $prompt.val(value); - module.query(); + $prompt + .val(value) + ; + }, + type: function(type) { + type = type || settings.type; + if(settings.type == 'category') { + $module.addClass(settings.type); + } }, buttonPressed: function() { $searchButton.addClass(className.pressed); @@ -359,55 +400,52 @@ $.fn.search = function(parameters) { searchTerm = module.get.value(), cache = module.read.cache(searchTerm) ; - if(cache) { - module.debug('Reading result for ' + searchTerm + ' from cache'); - module.save.results(cache.results); - module.addResults(cache.html); - } - else { - module.debug('Querying for ' + searchTerm); - if($.isPlainObject(settings.source) || $.isArray(settings.source)) { - module.search.local(searchTerm); + if( module.has.minimumCharacters() ) { + if(cache) { + module.debug('Reading result from cache', searchTerm); + module.save.results(cache.results); + module.addResults(cache.html); + module.inject.id(cache.results); } - else if( module.can.useAPI() ) { - if(settings.apiSettings) { - module.debug('Searching with specified API settings', settings.apiSettings); - module.search.remote(searchTerm); + else { + module.debug('Querying for', searchTerm); + if($.isPlainObject(settings.source) || $.isArray(settings.source)) { + module.search.local(searchTerm); } - else if($.api.settings.api.search !== undefined) { - module.debug('Searching with default search API endpoint'); + else if( module.can.useAPI() ) { module.search.remote(searchTerm); } else { - module.error(error.noEndpoint); + module.error(error.source); } + settings.onSearchQuery.call(element, searchTerm); } - else { - module.error(error.source); - } - settings.onSearchQuery.call(element, searchTerm); + } + else { + module.hideResults(); } }, search: { local: function(searchTerm) { var - searchResults = module.search.object(searchTerm, settings.content), + results = module.search.object(searchTerm, settings.content), searchHTML ; module.set.loading(); - module.save.results(searchResults); - module.debug('Returned local search results', searchResults); + module.save.results(results); + module.debug('Returned local search results', results); searchHTML = module.generateResults({ - results: searchResults + results: results }); module.remove.loading(); + module.addResults(searchHTML); + module.inject.id(results); module.write.cache(searchTerm, { html : searchHTML, - results : searchResults + results : results }); - module.addResults(searchHTML); }, remote: function(searchTerm) { var @@ -415,6 +453,9 @@ $.fn.search = function(parameters) { onSuccess : function(response) { module.parse.response.call(element, response, searchTerm); }, + onFailure: function() { + module.displayMessage(error.serverError); + }, urlData: { query: searchTerm } @@ -431,43 +472,60 @@ $.fn.search = function(parameters) { .api('query') ; }, - object: function(searchTerm, source) { + object: function(searchTerm, source, searchFields) { var - results = [], - fullTextResults = [], - searchFields = $.isArray(settings.searchFields) - ? settings.searchFields - : [settings.searchFields], - searchExp = searchTerm.replace(regExp.escape, '\\$&'), - searchRegExp = new RegExp(regExp.exact + searchExp, 'i') - ; + results = [], + fuzzyResults = [], + searchExp = searchTerm.toString().replace(regExp.escape, '\\$&'), + matchRegExp = new RegExp(regExp.beginsWith + searchExp, 'i'), + // avoid duplicates when pushing results + addResult = function(array, result) { + var + notResult = ($.inArray(result, results) == -1), + notFuzzyResult = ($.inArray(result, fuzzyResults) == -1) + ; + if(notResult && notFuzzyResult) { + array.push(result); + } + } + ; source = source || settings.source; + searchFields = (searchFields !== undefined) + ? searchFields + : settings.searchFields + ; + + // search fields should be array to loop correctly + if(!$.isArray(searchFields)) { + searchFields = [searchFields]; + } - // exit conditions on no source - if(source === undefined) { + // exit conditions if no source + if(source === undefined || source === false) { module.error(error.source); return []; } - // iterate through search fields in array order + // iterate through search fields looking for matches $.each(searchFields, function(index, field) { $.each(source, function(label, content) { var - fieldExists = (typeof content[field] == 'string'), - notAlreadyResult = ($.inArray(content, results) == -1 && $.inArray(content, fullTextResults) == -1) + fieldExists = (typeof content[field] == 'string') ; - if(fieldExists && notAlreadyResult) { - if( content[field].match(searchRegExp) ) { - results.push(content); + if(fieldExists) { + if( content[field].search(matchRegExp) !== -1) { + // content starts with value (first in results) + addResult(results, content); } else if(settings.searchFullText && module.fuzzySearch(searchTerm, content[field]) ) { - fullTextResults.push(content); + // content fuzzy matches (last in results) + addResult(fuzzyResults, content); } } }); }); - return $.merge(results, fullTextResults); + return $.merge(results, fuzzyResults); } }, @@ -476,6 +534,9 @@ $.fn.search = function(parameters) { termLength = term.length, queryLength = query.length ; + if(typeof query !== 'string') { + return false; + } query = query.toLowerCase(); term = term.toLowerCase(); if(queryLength > termLength) { @@ -506,27 +567,18 @@ $.fn.search = function(parameters) { module.verbose('Parsing server response', response); if(response !== undefined) { if(searchTerm !== undefined && response.results !== undefined) { + module.addResults(searchHTML); + module.inject.id(response.results); module.write.cache(searchTerm, { html : searchHTML, results : response.results }); module.save.results(response.results); - module.addResults(searchHTML); } } } }, - throttle: function() { - clearTimeout(module.timer); - if(module.has.minimumCharacters()) { - module.timer = setTimeout(module.query, settings.searchDelay); - } - else { - module.hideResults(); - } - }, - cancel: { query: function() { if( module.can.useAPI() ) { @@ -545,6 +597,23 @@ $.fn.search = function(parameters) { } }, + clear: { + cache: function(value) { + var + cache = $module.data(metadata.cache) + ; + if(!value) { + module.debug('Clearing cache', value); + $module.removeData(metadata.cache); + } + else if(value && cache && cache[value]) { + module.debug('Removing value from cache', value); + delete cache[value]; + $module.data(metadata.cache, cache); + } + } + }, + read: { cache: function(name) { var @@ -561,6 +630,94 @@ $.fn.search = function(parameters) { } }, + create: { + id: function(resultIndex, categoryIndex) { + var + resultID = (resultIndex + 1), // not zero indexed + categoryID = (categoryIndex + 1), + firstCharCode, + letterID, + id + ; + if(categoryIndex !== undefined) { + // start char code for "A" + letterID = String.fromCharCode(97 + categoryIndex); + id = letterID + resultID; + module.verbose('Creating category result id', id); + } + else { + id = resultID; + module.verbose('Creating result id', id); + } + return id; + }, + results: function() { + if($results.length === 0) { + $results = $('
') + .addClass(className.results) + .appendTo($module) + ; + } + } + }, + + inject: { + result: function(result, resultIndex, categoryIndex) { + module.verbose('Injecting result into results'); + var + $selectedResult = (categoryIndex !== undefined) + ? $results + .children().eq(categoryIndex) + .children(selector.result).eq(resultIndex) + : $results + .children(selector.result).eq(resultIndex) + ; + module.verbose('Injecting results metadata', $selectedResult); + $selectedResult + .data(metadata.result, result) + ; + }, + id: function(results) { + module.debug('Injecting unique ids into results'); + var + // since results may be object, we must use counters + categoryIndex = 0, + resultIndex = 0 + ; + if(settings.type === 'category') { + // iterate through each category result + $.each(results, function(index, category) { + resultIndex = 0; + $.each(category.results, function(index, value) { + var + result = category.results[index] + ; + if(result.id === undefined) { + result.id = module.create.id(resultIndex, categoryIndex); + } + module.inject.result(result, resultIndex, categoryIndex); + resultIndex++; + }); + categoryIndex++; + }); + } + else { + // top level + $.each(results, function(index, value) { + var + result = results[index] + ; + if(result.id === undefined) { + result.id = module.create.id(resultIndex); + } + module.inject.result(result, resultIndex); + resultIndex++; + }); + } + return results; + } + }, + save: { results: function(results) { module.verbose('Saving current search results to metadata', results); @@ -605,6 +762,8 @@ $.fn.search = function(parameters) { $results .transition({ animation : settings.transition + ' in', + debug : settings.debug, + verbose : settings.verbose, duration : settings.duration, queue : true }) @@ -627,6 +786,8 @@ $.fn.search = function(parameters) { $results .transition({ animation : settings.transition + ' out', + debug : settings.debug, + verbose : settings.verbose, duration : settings.duration, queue : true }) @@ -751,7 +912,7 @@ $.fn.search = function(parameters) { }); } clearTimeout(module.performance.timer); - module.performance.timer = setTimeout(module.performance.display, 100); + module.performance.timer = setTimeout(module.performance.display, 500); }, display: function() { var @@ -863,36 +1024,55 @@ $.fn.search = function(parameters) { $.fn.search.settings = { - name : 'Search Module', + name : 'Search', namespace : 'search', debug : false, - verbose : true, + verbose : false, performance : true, type : 'standard', + // template to use (specified in settings.templates) + minCharacters : 1, + // minimum characters required to search - // api config apiSettings : false, + // API config source : false, + // object to search + searchFields : [ 'title', 'description' ], + // fields to search + searchFullText : true, + // whether to include fuzzy results in local search + + automatic : true, + // whether to add events to prompt automatically - automatic : 'true', hideDelay : 0, - searchDelay : 100, + // delay before hiding menu after blur + + searchDelay : 200, + // delay before searching + maxResults : 7, + // maximum results returned from local + cache : true, + // whether to store lookups in local cache + // transition settings transition : 'scale', - duration : 300, + duration : 200, easing : 'easeOutExpo', + // callbacks onSelect : false, onResultsAdd : false, @@ -907,6 +1087,7 @@ $.fn.search.settings = { empty : 'empty', focus : 'focus', loading : 'loading', + results : 'results', pressed : 'down' }, @@ -916,19 +1097,20 @@ $.fn.search.settings = { logging : 'Error in debug logging, exiting.', noEndpoint : 'No search endpoint was specified', noTemplate : 'A valid template name was not specified.', - serverError : 'There was an issue with querying the server.', + serverError : 'There was an issue querying the server.', maxResults : 'Results must be an array to use maxResults setting', method : 'The method you called is not defined.' }, metadata: { cache : 'cache', - results : 'results' + results : 'results', + result : 'result' }, regExp: { - escape : /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, - exact : '(?:\s|^)' + escape : /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, + beginsWith : '(?:\s|^)' }, selector : { diff --git a/search.min.css b/search.min.css index d59cc9f..0caeab8 100755 --- a/search.min.css +++ b/search.min.css @@ -1,10 +1,10 @@ /*! - * # Semantic UI 1.12.3 - Search + * # Semantic UI 2.0.0 - Search * http://github.com/semantic-org/semantic-ui/ * * - * Copyright 2014 Contributors + * Copyright 2015 Contributors * Released under the MIT license * http://opensource.org/licenses/MIT * - */.ui.search{position:relative}.ui.search>.prompt{margin:0;outline:0;-webkit-appearance:none;-webkit-tap-highlight-color:rgba(255,255,255,0);text-shadow:none;font-style:normal;font-weight:400;line-height:1.2;padding:.68571em 1em;font-size:1em;background:#fff;border:1px solid rgba(39,41,43,.15);color:rgba(0,0,0,.8);box-shadow:0 0 0 0 transparent inset;-webkit-transition:background-color .2s ease,color .2s ease,box-shadow .2s ease,border-color .2s ease;transition:background-color .2s ease,color .2s ease,box-shadow .2s ease,border-color .2s ease}.ui.search .prompt{border-radius:500rem}.ui.search .prompt~.search.icon{cursor:pointer}.ui.search>.results{display:none;position:absolute;top:100%;left:0;background:#fff;margin-top:.5em;width:16em;border-radius:.25em;box-shadow:0 1px 3px 1px rgba(0,0,0,.2);z-index:998}.ui.search>.results .result{cursor:pointer;display:block;overflow:hidden;font-size:1em;padding:.5em 1em;color:rgba(0,0,0,.8);line-height:1.33;border-bottom:1px solid rgba(39,41,43,.15)}.ui.search>.results .result:last-child{border-bottom:none}.ui.search>.results .result .image{float:right;overflow:hidden;background:0 0;width:5em;height:3em;border-radius:.25em}.ui.search>.results .result .image img{display:block;width:auto;height:100%}.ui.search>.results .result .image+.content{margin:0 6em 0 0}.ui.search>.results .result .title{font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;font-weight:700;font-size:1em;color:rgba(0,0,0,.85)}.ui.search>.results .result .description{margin-top:0;font-size:.9285em;color:rgba(0,0,0,.4)}.ui.search>.results .result .price{float:right;color:#5bbd72}.ui.search>.results>.message{padding:1em}.ui.search>.results>.message .header{font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;font-size:1.1428em;font-weight:700;color:rgba(0,0,0,.8)}.ui.search>.results>.message .description{margin-top:.25rem;font-size:1em;color:rgba(0,0,0,.8)}.ui.search>.results>.action{display:block;border-top:none;background:#f0f0f0;padding:.5em 1em;color:rgba(0,0,0,.8);font-weight:700;text-align:center}.ui.loading.search .input>.icon:before{position:absolute;content:'';top:50%;left:50%;margin:-.64285em 0 0 -.64285em;width:1.2857em;height:1.2857em;border-radius:500rem;border:.2em solid rgba(0,0,0,.1)}.ui.loading.search .input>.icon:after{position:absolute;content:'';top:50%;left:50%;margin:-.64285em 0 0 -.64285em;width:1.2857em;height:1.2857em;-webkit-animation:button-spin .6s linear;animation:button-spin .6s linear;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite;border-radius:500rem;border-color:#aaa transparent transparent;border-style:solid;border-width:.2em;box-shadow:0 0 0 1px transparent}.ui.category.search>.results .category .result:hover,.ui.search>.results .result:hover{background:#fafafa}.ui.search .action:hover{background:#e0e0e0}.ui.search>.results .category.active{background:#f0f0f0}.ui.search>.results .category.active>.name{color:rgba(0,0,0,.8)}.ui.category.search>.results .category .result.active,.ui.search>.results .result.active{position:relative;border-left-color:transparent;background:#f0f0f0;box-shadow:3px 0 3px 0 rgba(39,41,43,.15)}.ui.search>.results .result.active .description,.ui.search>.results .result.active .title{color:rgba(0,0,0,.85)}.ui.category.search .results{width:28em}.ui.category.search>.results .category{background:#f0f0f0;box-shadow:none;border-bottom:1px solid rgba(39,41,43,.15);-webkit-transition:background .2s ease,border-color .2s ease;transition:background .2s ease,border-color .2s ease}.ui.category.search>.results .category:last-child{border-bottom:none}.ui.category.search>.results .category .result{background:#fff;margin-left:100px;border-left:1px solid rgba(39,41,43,.15);border-bottom:1px solid rgba(39,41,43,.15);-webkit-transition:background .2s ease,border-color .2s ease;transition:background .2s ease,border-color .2s ease}.ui.category.search>.results .category .result:last-child{border-bottom:none}.ui.category.search>.results .category>.name{width:100px;background:#f0f0f0;font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;font-size:1em;float:1em;float:left;padding:.4em 1em;font-weight:700;color:rgba(0,0,0,.4)}.ui[class*="left aligned"].search>.results{right:auto;left:0}.ui[class*="right aligned"].search>.results{right:0;left:auto}.ui.fluid.search .results{width:100%}.ui.search{font-size:1em}.ui.large.search{font-size:1.1em} \ No newline at end of file + */.ui.search{position:relative}.ui.search>.prompt{margin:0;outline:0;-webkit-appearance:none;-webkit-tap-highlight-color:rgba(255,255,255,0);text-shadow:none;font-style:normal;font-weight:400;line-height:1.2142em;padding:.67861429em 1em;font-size:1em;background:#fff;border:1px solid rgba(34,36,38,.15);color:rgba(0,0,0,.87);box-shadow:0 0 0 0 transparent inset;-webkit-transition:background-color .1s ease,color .1s ease,box-shadow .1s ease,border-color .1s ease;transition:background-color .1s ease,color .1s ease,box-shadow .1s ease,border-color .1s ease}.ui.search .prompt{border-radius:500rem}.ui.search .prompt~.search.icon{cursor:pointer}.ui.search>.results{display:none;position:absolute;top:100%;left:0;-webkit-transform-origin:center top;-ms-transform-origin:center top;transform-origin:center top;background:#fff;margin-top:.5em;width:18em;border-radius:.28571429rem;box-shadow:0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.08);border:1px solid #d4d4d5;z-index:998}.ui.search>.results>:first-child{border-radius:.28571429rem .28571429rem 0 0}.ui.search>.results>:last-child{border-radius:0 0 .28571429rem .28571429rem}.ui.search>.results .result{cursor:pointer;display:block;overflow:hidden;font-size:1em;padding:.85714286em 1.14285714em;color:rgba(0,0,0,.87);line-height:1.33;border-bottom:1px solid rgba(34,36,38,.1)}.ui.search>.results .result:last-child{border-bottom:none!important}.ui.search>.results .result .image{float:right;overflow:hidden;background:0 0;width:5em;height:3em;border-radius:.25em}.ui.search>.results .result .image img{display:block;width:auto;height:100%}.ui.search>.results .result .image+.content{margin:0 6em 0 0}.ui.search>.results .result .title{margin:-.14285em 0 0;font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;font-weight:700;font-size:1em;color:rgba(0,0,0,.85)}.ui.search>.results .result .description{margin-top:0;font-size:.92857143em;color:rgba(0,0,0,.4)}.ui.search>.results .result .price{float:right;color:#21ba45}.ui.search>.results>.message{padding:1em}.ui.search>.results>.message .header{font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;font-size:1rem;font-weight:700;color:rgba(0,0,0,.87)}.ui.search>.results>.message .description{margin-top:.25rem;font-size:1em;color:rgba(0,0,0,.87)}.ui.search>.results>.action{display:block;border-top:none;background:#f3f4f5;padding:.92857143em 1em;color:rgba(0,0,0,.87);font-weight:700;text-align:center}.ui.search>.prompt:focus{border-color:rgba(34,36,38,.35);background:#fff;color:rgba(0,0,0,.95)}.ui.loading.search .input>i.icon:before{position:absolute;content:'';top:50%;left:50%;margin:-.64285714em 0 0 -.64285714em;width:1.28571429em;height:1.28571429em;border-radius:500rem;border:.2em solid rgba(0,0,0,.1)}.ui.loading.search .input>i.icon:after{position:absolute;content:'';top:50%;left:50%;margin:-.64285714em 0 0 -.64285714em;width:1.28571429em;height:1.28571429em;-webkit-animation:button-spin .6s linear;animation:button-spin .6s linear;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite;border-radius:500rem;border-color:#767676 transparent transparent;border-style:solid;border-width:.2em;box-shadow:0 0 0 1px transparent}.ui.category.search>.results .category .result:hover,.ui.search>.results .result:hover{background:#f9fafb}.ui.search .action:hover{background:#e0e0e0}.ui.category.search>.results .category.active{background:#f3f4f5}.ui.category.search>.results .category.active>.name{color:rgba(0,0,0,.87)}.ui.category.search>.results .category .result.active,.ui.search>.results .result.active{position:relative;border-left-color:rgba(34,36,38,.1);background:#f3f4f5;box-shadow:none}.ui.search>.results .result.active .description,.ui.search>.results .result.active .title{color:rgba(0,0,0,.85)}.ui.category.search .results{width:28em}.ui.category.search>.results .category{background:#f3f4f5;box-shadow:none;border-bottom:1px solid rgba(34,36,38,.1);-webkit-transition:background .1s ease,border-color .1s ease;transition:background .1s ease,border-color .1s ease}.ui.category.search>.results .category:last-child{border-bottom:none}.ui.category.search>.results .category:first-child .name+.result{border-radius:0 .28571429rem 0 0}.ui.category.search>.results .category .result{background:#fff;margin-left:100px;border-left:1px solid rgba(34,36,38,.15);border-bottom:1px solid rgba(34,36,38,.1);-webkit-transition:background .1s ease,border-color .1s ease;transition:background .1s ease,border-color .1s ease;padding:.85714286em 1.14285714em}.ui.category.search>.results .category:last-child .result:last-child{border-radius:0 0 .28571429rem;border-bottom:none}.ui.category.search>.results .category>.name{width:100px;background:0 0;font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;font-size:1em;float:1em;float:left;padding:.4em 1em;font-weight:700;color:rgba(0,0,0,.4)}.ui[class*="left aligned"].search>.results{right:auto;left:0}.ui[class*="right aligned"].search>.results{right:0;left:auto}.ui.fluid.search .results{width:100%}.ui.mini.search{font-size:.71428571em}.ui.small.search{font-size:.92857143em}.ui.search{font-size:1em}.ui.large.search{font-size:1.14285714em}.ui.big.search{font-size:1.28571429em}.ui.huge.search{font-size:1.42857143em}.ui.massive.search{font-size:1.71428571em} \ No newline at end of file diff --git a/search.min.js b/search.min.js index bcb5232..3cabe73 100755 --- a/search.min.js +++ b/search.min.js @@ -1,11 +1,11 @@ /*! - * # Semantic UI 1.12.3 - Search + * # Semantic UI 2.0.0 - Search * http://github.com/semantic-org/semantic-ui/ * * - * Copyright 2014 Contributors + * Copyright 2015 Contributors * Released under the MIT license * http://opensource.org/licenses/MIT * */ -!function(e,t,s,n){"use strict";e.fn.search=function(r){var a,i=e(this),o=i.selector||"",c=(new Date).getTime(),u=[],l=arguments[0],d="string"==typeof l,f=[].slice.call(arguments,1);return e(this).each(function(){var p,g=e.extend(!0,{},e.fn.search.settings,r),h=g.className,m=g.metadata,v=g.regExp,b=g.selector,y=g.error,w=g.namespace,R="."+w,C=w+"-module",x=e(this),A=x.find(b.prompt),k=x.find(b.searchButton),S=x.find(b.results),q=(x.find(b.result),x.find(b.category),this),E=x.data(C);p={initialize:function(){p.verbose("Initializing module");var e=A[0],t=e!==n&&e.oninput!==n?"input":e!==n&&e.onpropertychange!==n?"propertychange":"keyup";g.automatic&&A.on(t+R,p.throttle).attr("autocomplete","off"),A.on("focus"+R,p.event.focus).on("blur"+R,p.event.blur).on("keydown"+R,p.handleKeyboard),k.on("click"+R,p.query),S.on("mousedown"+R,p.event.result.mousedown).on("mouseup"+R,p.event.result.mouseup).on("click"+R,b.result,p.event.result.click),p.instantiate()},instantiate:function(){p.verbose("Storing instance of module",p),E=p,x.data(C,p)},destroy:function(){p.verbose("Destroying instance"),x.removeData(C),A.off(R),k.off(R),S.off(R)},event:{focus:function(){p.set.focus(),clearTimeout(p.timer),p.throttle(),p.has.minimumCharacters()&&p.showResults()},blur:function(e){var t=s.activeElement===this;t||p.resultsClicked||(p.cancel.query(),p.remove.focus(),p.timer=setTimeout(p.hideResults,g.hideDelay))},result:{mousedown:function(){p.resultsClicked=!0},mouseup:function(){p.resultsClicked=!1},click:function(s){p.debug("Search result selected");var n=e(this),r=n.find(b.title).eq(0),a=n.find("a[href]").eq(0),i=a.attr("href")||!1,o=a.attr("target")||!1,c=(r.html(),r.length>0?r.text():!1),u=p.get.results(),l=p.get.result(c,u);return e.isFunction(g.onSelect)&&g.onSelect.call(q,l,u)===!1?void p.debug("Custom onSelect callback cancelled default select action"):(p.hideResults(),c&&p.set.value(c),void(i&&(p.verbose("Opening search link found in result",a),"_blank"==o||s.ctrlKey?t.open(i):t.location.href=i)))}}},handleKeyboard:function(e){var t,s=x.find(b.result),n=x.find(b.category),r=s.index(s.filter("."+h.active)),a=s.length,i=e.which,o={backspace:8,enter:13,escape:27,upArrow:38,downArrow:40};if(i==o.escape&&(p.verbose("Escape key pressed, blurring search field"),A.trigger("blur")),p.is.visible())if(i==o.enter){if(p.verbose("Enter key pressed, selecting active result"),s.filter("."+h.active).length>0)return p.event.result.click.call(s.filter("."+h.active),e),e.preventDefault(),!1}else i==o.upArrow?(p.verbose("Up key pressed, changing active result"),t=0>r-1?r:r-1,n.removeClass(h.active),s.removeClass(h.active).eq(t).addClass(h.active).closest(n).addClass(h.active),e.preventDefault()):i==o.downArrow&&(p.verbose("Down key pressed, changing active result"),t=r+1>=a?r:r+1,n.removeClass(h.active),s.removeClass(h.active).eq(t).addClass(h.active).closest(n).addClass(h.active),e.preventDefault());else i==o.enter&&(p.verbose("Enter key pressed, executing query"),p.query(),p.set.buttonPressed(),A.one("keyup",p.remove.buttonFocus))},setup:{api:function(){var e={on:!1,action:"search",onFailure:p.error};p.verbose("First request, initializing API"),x.api(e)}},can:{useAPI:function(){return e.fn.api!==n},transition:function(){return g.transition&&e.fn.transition!==n&&x.transition("is supported")}},is:{empty:function(){return""===S.html()},visible:function(){return S.filter(":visible").length>0},focused:function(){return A.filter(":focus").length>0}},get:{value:function(){return A.val()},results:function(){var e=x.data(m.results);return e},result:function(t,s){var n=!1;return t=t||p.get.value(),s=s||p.get.results(),"category"===g.type?(p.debug("Finding result that matches",t),e.each(s,function(s,r){return e.isArray(r.results)&&(n=p.search.object(t,r.results)[0],n&&n.length>0)?!0:void 0})):(p.debug("Finding result in results object",t),n=p.search.object(t,s)[0]),n}},set:{focus:function(){x.addClass(h.focus)},loading:function(){x.addClass(h.loading)},value:function(e){p.verbose("Setting search input value",e),A.val(e),p.query()},buttonPressed:function(){k.addClass(h.pressed)}},remove:{loading:function(){x.removeClass(h.loading)},focus:function(){x.removeClass(h.focus)},buttonPressed:function(){k.removeClass(h.pressed)}},query:function(){var t=p.get.value(),s=p.read.cache(t);s?(p.debug("Reading result for "+t+" from cache"),p.save.results(s.results),p.addResults(s.html)):(p.debug("Querying for "+t),e.isPlainObject(g.source)||e.isArray(g.source)?p.search.local(t):p.can.useAPI()?g.apiSettings?(p.debug("Searching with specified API settings",g.apiSettings),p.search.remote(t)):e.api.settings.api.search!==n?(p.debug("Searching with default search API endpoint"),p.search.remote(t)):p.error(y.noEndpoint):p.error(y.source),g.onSearchQuery.call(q,t))},search:{local:function(e){var t,s=p.search.object(e,g.content);p.set.loading(),p.save.results(s),p.debug("Returned local search results",s),t=p.generateResults({results:s}),p.remove.loading(),p.write.cache(e,{html:t,results:s}),p.addResults(t)},remote:function(t){var s={onSuccess:function(e){p.parse.response.call(q,e,t)},urlData:{query:t}};x.api("get request")||p.setup.api(),e.extend(!0,s,g.apiSettings),p.debug("Executing search",s),p.cancel.query(),x.api("setting",s).api("query")},object:function(t,s){var r=[],a=[],i=e.isArray(g.searchFields)?g.searchFields:[g.searchFields],o=t.replace(v.escape,"\\$&"),c=new RegExp(v.exact+o,"i");return s=s||g.source,s===n?(p.error(y.source),[]):(e.each(i,function(n,i){e.each(s,function(s,n){var o="string"==typeof n[i],u=-1==e.inArray(n,r)&&-1==e.inArray(n,a);o&&u&&(n[i].match(c)?r.push(n):g.searchFullText&&p.fuzzySearch(t,n[i])&&a.push(n))})}),e.merge(r,a))}},fuzzySearch:function(e,t){var s=t.length,n=e.length;if(e=e.toLowerCase(),t=t.toLowerCase(),n>s)return!1;if(n===s)return e===t;e:for(var r=0,a=0;n>r;r++){for(var i=e.charCodeAt(r);s>a;)if(t.charCodeAt(a++)===i)continue e;return!1}return!0},parse:{response:function(e,t){var s=p.generateResults(e);p.verbose("Parsing server response",e),e!==n&&t!==n&&e.results!==n&&(p.write.cache(t,{html:s,results:e.results}),p.save.results(e.results),p.addResults(s))}},throttle:function(){clearTimeout(p.timer),p.has.minimumCharacters()?p.timer=setTimeout(p.query,g.searchDelay):p.hideResults()},cancel:{query:function(){p.can.useAPI()&&x.api("abort")}},has:{minimumCharacters:function(){var e=p.get.value(),t=e.length;return t>=g.minCharacters}},read:{cache:function(e){var t=x.data(m.cache);return g.cache?(p.verbose("Checking cache for generated html for query",e),"object"==typeof t&&t[e]!==n?t[e]:!1):!1}},save:{results:function(e){p.verbose("Saving current search results to metadata",e),x.data(m.results,e)}},write:{cache:function(e,t){var s=x.data(m.cache)!==n?x.data(m.cache):{};g.cache&&(p.verbose("Writing generated html to cache",e,t),s[e]=t,x.data(m.cache,s))}},addResults:function(t){return e.isFunction(g.onResultsAdd)&&g.onResultsAdd.call(S,t)===!1?(p.debug("onResultsAdd callback cancelled default action"),!1):(S.html(t),void p.showResults())},showResults:function(){p.is.visible()||!p.is.focused()||p.is.empty()||(p.can.transition()?(p.debug("Showing results with css animations"),S.transition({animation:g.transition+" in",duration:g.duration,queue:!0})):(p.debug("Showing results with javascript"),S.stop().fadeIn(g.duration,g.easing)),g.onResultsOpen.call(S))},hideResults:function(){p.is.visible()&&(p.can.transition()?(p.debug("Hiding results with css animations"),S.transition({animation:g.transition+" out",duration:g.duration,queue:!0})):(p.debug("Hiding results with javascript"),S.stop().fadeOut(g.duration,g.easing)),g.onResultsClose.call(S))},generateResults:function(t){p.debug("Generating html from response",t);var s=g.templates[g.type],n=e.isPlainObject(t.results)&&!e.isEmptyObject(t.results),r=e.isArray(t.results)&&t.results.length>0,a="";return n||r?(g.maxResults>0&&(n?"standard"==g.type&&p.error(y.maxResults):t.results=t.results.slice(0,g.maxResults)),e.isFunction(s)?a=s(t):p.error(y.noTemplate,!1)):a=p.displayMessage(y.noResults,"empty"),g.onResults.call(q,t),a},displayMessage:function(e,t){return t=t||"standard",p.debug("Displaying message",e,t),p.addResults(g.templates.message(e,t)),g.templates.message(e,t)},setting:function(t,s){if(e.isPlainObject(t))e.extend(!0,g,t);else{if(s===n)return g[t];g[t]=s}},internal:function(t,s){if(e.isPlainObject(t))e.extend(!0,p,t);else{if(s===n)return p[t];p[t]=s}},debug:function(){g.debug&&(g.performance?p.performance.log(arguments):(p.debug=Function.prototype.bind.call(console.info,console,g.name+":"),p.debug.apply(console,arguments)))},verbose:function(){g.verbose&&g.debug&&(g.performance?p.performance.log(arguments):(p.verbose=Function.prototype.bind.call(console.info,console,g.name+":"),p.verbose.apply(console,arguments)))},error:function(){p.error=Function.prototype.bind.call(console.error,console,g.name+":"),p.error.apply(console,arguments)},performance:{log:function(e){var t,s,n;g.performance&&(t=(new Date).getTime(),n=c||t,s=t-n,c=t,u.push({Name:e[0],Arguments:[].slice.call(e,1)||"",Element:q,"Execution Time":s})),clearTimeout(p.performance.timer),p.performance.timer=setTimeout(p.performance.display,100)},display:function(){var t=g.name+":",s=0;c=!1,clearTimeout(p.performance.timer),e.each(u,function(e,t){s+=t["Execution Time"]}),t+=" "+s+"ms",o&&(t+=" '"+o+"'"),i.length>1&&(t+=" ("+i.length+")"),(console.group!==n||console.table!==n)&&u.length>0&&(console.groupCollapsed(t),console.table?console.table(u):e.each(u,function(e,t){console.log(t.Name+": "+t["Execution Time"]+"ms")}),console.groupEnd()),u=[]}},invoke:function(t,s,r){var i,o,c,u=E;return s=s||f,r=q||r,"string"==typeof t&&u!==n&&(t=t.split(/[\. ]/),i=t.length-1,e.each(t,function(s,r){var a=s!=i?r+t[s+1].charAt(0).toUpperCase()+t[s+1].slice(1):t;if(e.isPlainObject(u[a])&&s!=i)u=u[a];else{if(u[a]!==n)return o=u[a],!1;if(!e.isPlainObject(u[r])||s==i)return u[r]!==n?(o=u[r],!1):!1;u=u[r]}})),e.isFunction(o)?c=o.apply(r,s):o!==n&&(c=o),e.isArray(a)?a.push(c):a!==n?a=[a,c]:c!==n&&(a=c),o}},d?(E===n&&p.initialize(),p.invoke(l)):(E!==n&&E.invoke("destroy"),p.initialize())}),a!==n?a:this},e.fn.search.settings={name:"Search Module",namespace:"search",debug:!1,verbose:!0,performance:!0,type:"standard",minCharacters:1,apiSettings:!1,source:!1,searchFields:["title","description"],searchFullText:!0,automatic:"true",hideDelay:0,searchDelay:100,maxResults:7,cache:!0,transition:"scale",duration:300,easing:"easeOutExpo",onSelect:!1,onResultsAdd:!1,onSearchQuery:function(){},onResults:function(e){},onResultsOpen:function(){},onResultsClose:function(){},className:{active:"active",empty:"empty",focus:"focus",loading:"loading",pressed:"down"},error:{source:"Cannot search. No source used, and Semantic API module was not included",noResults:"Your search returned no results",logging:"Error in debug logging, exiting.",noEndpoint:"No search endpoint was specified",noTemplate:"A valid template name was not specified.",serverError:"There was an issue with querying the server.",maxResults:"Results must be an array to use maxResults setting",method:"The method you called is not defined."},metadata:{cache:"cache",results:"results"},regExp:{escape:/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,exact:"(?:s|^)"},selector:{prompt:".prompt",searchButton:".search.button",results:".results",category:".category",result:".result",title:".title, .name"},templates:{escape:function(e){var t=/[&<>"'`]/g,s=/[&<>"'`]/,n={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"},r=function(e){return n[e]};return s.test(e)?e.replace(t,r):e},message:function(e,t){var s="";return e!==n&&t!==n&&(s+='
',s+="empty"==t?'
No Results
'+e+'
':'
'+e+"
",s+="
"),s},category:function(t){var s="",r=e.fn.search.settings.templates.escape;return t.results!==n?(e.each(t.results,function(t,a){a.results!==n&&a.results.length>0&&(s+='
'+a.name+"
",e.each(a.results,function(e,t){s+='
',t.url&&(s+=''),t.image!==n&&(t.image=r(t.image),s+='
'),s+='
',t.price!==n&&(t.price=r(t.price),s+='
'+t.price+"
"),t.title!==n&&(t.title=r(t.title),s+='
'+t.title+"
"),t.description!==n&&(s+='
'+t.description+"
"),s+="
"}),s+="
")}),t.action&&(s+=''+t.action.text+""),s):!1},standard:function(t){var s="";return t.results!==n?(e.each(t.results,function(e,t){s+=t.url?'':'',t.image!==n&&(s+='
'),s+='
',t.price!==n&&(s+='
'+t.price+"
"),t.title!==n&&(s+='
'+t.title+"
"),t.description!==n&&(s+='
'+t.description+"
"),s+="
",s+="
"}),t.action&&(s+=''+t.action.text+""),s):!1}}}}(jQuery,window,document); \ No newline at end of file +!function(e,t,s,r){"use strict";e.fn.search=function(n){var a,i=e(this),c=i.selector||"",o=(new Date).getTime(),u=[],l=arguments[0],d="string"==typeof l,g=[].slice.call(arguments,1);return e(this).each(function(){var f,p=e.isPlainObject(n)?e.extend(!0,{},e.fn.search.settings,n):e.extend({},e.fn.search.settings),v=p.className,h=p.metadata,m=p.regExp,b=p.selector,y=p.error,C=p.namespace,R="."+C,w=C+"-module",x=e(this),j=x.find(b.prompt),q=x.find(b.searchButton),k=x.find(b.results),A=(x.find(b.result),x.find(b.category),this),E=x.data(w);f={initialize:function(){f.verbose("Initializing module"),f.determine.searchFields(),f.bind.events(),f.set.type(),f.create.results(),f.instantiate()},instantiate:function(){f.verbose("Storing instance of module",f),E=f,x.data(w,f)},destroy:function(){f.verbose("Destroying instance"),x.off(R).removeData(w)},bind:{events:function(){f.verbose("Binding events to search"),p.automatic&&(x.on(f.get.inputEvent()+R,b.prompt,f.event.input),j.attr("autocomplete","off")),x.on("focus"+R,b.prompt,f.event.focus).on("blur"+R,b.prompt,f.event.blur).on("keydown"+R,b.prompt,f.handleKeyboard).on("click"+R,b.searchButton,f.query).on("mousedown"+R,b.results,f.event.result.mousedown).on("mouseup"+R,b.results,f.event.result.mouseup).on("click"+R,b.result,f.event.result.click)}},determine:{searchFields:function(){n&&n.searchFields!==r&&(p.searchFields=n.searchFields)}},event:{input:function(){clearTimeout(f.timer),f.timer=setTimeout(f.query,p.searchDelay)},focus:function(){f.set.focus(),f.has.minimumCharacters()&&(f.query(),f.showResults())},blur:function(e){var t=s.activeElement===this;t||f.resultsClicked||(f.cancel.query(),f.remove.focus(),f.timer=setTimeout(f.hideResults,p.hideDelay))},result:{mousedown:function(){f.resultsClicked=!0},mouseup:function(){f.resultsClicked=!1},click:function(s){f.debug("Search result selected");var r=e(this),n=r.find(b.title).eq(0),a=r.find("a[href]").eq(0),i=a.attr("href")||!1,c=a.attr("target")||!1,o=(n.html(),n.length>0?n.text():!1),u=f.get.results(),l=r.data(h.result)||f.get.result(o,u);return e.isFunction(p.onSelect)&&p.onSelect.call(A,l,u)===!1?void f.debug("Custom onSelect callback cancelled default select action"):(f.hideResults(),o&&f.set.value(o),void(i&&(f.verbose("Opening search link found in result",a),"_blank"==c||s.ctrlKey?t.open(i):t.location.href=i)))}}},handleKeyboard:function(e){var t,s=x.find(b.result),r=x.find(b.category),n=s.index(s.filter("."+v.active)),a=s.length,i=e.which,c={backspace:8,enter:13,escape:27,upArrow:38,downArrow:40};if(i==c.escape&&(f.verbose("Escape key pressed, blurring search field"),j.trigger("blur")),f.is.visible())if(i==c.enter){if(f.verbose("Enter key pressed, selecting active result"),s.filter("."+v.active).length>0)return f.event.result.click.call(s.filter("."+v.active),e),e.preventDefault(),!1}else i==c.upArrow?(f.verbose("Up key pressed, changing active result"),t=0>n-1?n:n-1,r.removeClass(v.active),s.removeClass(v.active).eq(t).addClass(v.active).closest(r).addClass(v.active),e.preventDefault()):i==c.downArrow&&(f.verbose("Down key pressed, changing active result"),t=n+1>=a?n:n+1,r.removeClass(v.active),s.removeClass(v.active).eq(t).addClass(v.active).closest(r).addClass(v.active),e.preventDefault());else i==c.enter&&(f.verbose("Enter key pressed, executing query"),f.query(),f.set.buttonPressed(),j.one("keyup",f.remove.buttonFocus))},setup:{api:function(){var e={debug:p.debug,on:!1,cache:"local",action:"search",onError:f.error};f.verbose("First request, initializing API"),x.api(e)}},can:{useAPI:function(){return e.fn.api!==r},transition:function(){return p.transition&&e.fn.transition!==r&&x.transition("is supported")}},is:{empty:function(){return""===k.html()},visible:function(){return k.filter(":visible").length>0},focused:function(){return j.filter(":focus").length>0}},get:{inputEvent:function(){var e=j[0],t=e!==r&&e.oninput!==r?"input":e!==r&&e.onpropertychange!==r?"propertychange":"keyup";return t},value:function(){return j.val()},results:function(){var e=x.data(h.results);return e},result:function(t,s){var n=["title","id"],a=!1;return t=t!==r?t:f.get.value(),s=s!==r?s:f.get.results(),"category"===p.type?(f.debug("Finding result that matches",t),e.each(s,function(s,r){return e.isArray(r.results)&&(a=f.search.object(t,r.results,n)[0])?!1:void 0})):(f.debug("Finding result in results object",t),a=f.search.object(t,s,n)[0]),a||!1}},set:{focus:function(){x.addClass(v.focus)},loading:function(){x.addClass(v.loading)},value:function(e){f.verbose("Setting search input value",e),j.val(e)},type:function(e){e=e||p.type,"category"==p.type&&x.addClass(p.type)},buttonPressed:function(){q.addClass(v.pressed)}},remove:{loading:function(){x.removeClass(v.loading)},focus:function(){x.removeClass(v.focus)},buttonPressed:function(){q.removeClass(v.pressed)}},query:function(){var t=f.get.value(),s=f.read.cache(t);f.has.minimumCharacters()?s?(f.debug("Reading result from cache",t),f.save.results(s.results),f.addResults(s.html),f.inject.id(s.results)):(f.debug("Querying for",t),e.isPlainObject(p.source)||e.isArray(p.source)?f.search.local(t):f.can.useAPI()?f.search.remote(t):f.error(y.source),p.onSearchQuery.call(A,t)):f.hideResults()},search:{local:function(e){var t,s=f.search.object(e,p.content);f.set.loading(),f.save.results(s),f.debug("Returned local search results",s),t=f.generateResults({results:s}),f.remove.loading(),f.addResults(t),f.inject.id(s),f.write.cache(e,{html:t,results:s})},remote:function(t){var s={onSuccess:function(e){f.parse.response.call(A,e,t)},onFailure:function(){f.displayMessage(y.serverError)},urlData:{query:t}};x.api("get request")||f.setup.api(),e.extend(!0,s,p.apiSettings),f.debug("Executing search",s),f.cancel.query(),x.api("setting",s).api("query")},object:function(t,s,n){var a=[],i=[],c=t.toString().replace(m.escape,"\\$&"),o=new RegExp(m.beginsWith+c,"i"),u=function(t,s){var r=-1==e.inArray(s,a),n=-1==e.inArray(s,i);r&&n&&t.push(s)};return s=s||p.source,n=n!==r?n:p.searchFields,e.isArray(n)||(n=[n]),s===r||s===!1?(f.error(y.source),[]):(e.each(n,function(r,n){e.each(s,function(e,s){var r="string"==typeof s[n];r&&(-1!==s[n].search(o)?u(a,s):p.searchFullText&&f.fuzzySearch(t,s[n])&&u(i,s))})}),e.merge(a,i))}},fuzzySearch:function(e,t){var s=t.length,r=e.length;if("string"!=typeof e)return!1;if(e=e.toLowerCase(),t=t.toLowerCase(),r>s)return!1;if(r===s)return e===t;e:for(var n=0,a=0;r>n;n++){for(var i=e.charCodeAt(n);s>a;)if(t.charCodeAt(a++)===i)continue e;return!1}return!0},parse:{response:function(e,t){var s=f.generateResults(e);f.verbose("Parsing server response",e),e!==r&&t!==r&&e.results!==r&&(f.addResults(s),f.inject.id(e.results),f.write.cache(t,{html:s,results:e.results}),f.save.results(e.results))}},cancel:{query:function(){f.can.useAPI()&&x.api("abort")}},has:{minimumCharacters:function(){var e=f.get.value(),t=e.length;return t>=p.minCharacters}},clear:{cache:function(e){var t=x.data(h.cache);e?e&&t&&t[e]&&(f.debug("Removing value from cache",e),delete t[e],x.data(h.cache,t)):(f.debug("Clearing cache",e),x.removeData(h.cache))}},read:{cache:function(e){var t=x.data(h.cache);return p.cache?(f.verbose("Checking cache for generated html for query",e),"object"==typeof t&&t[e]!==r?t[e]:!1):!1}},create:{id:function(e,t){var s,n,a=e+1;return t!==r?(s=String.fromCharCode(97+t),n=s+a,f.verbose("Creating category result id",n)):(n=a,f.verbose("Creating result id",n)),n},results:function(){0===k.length&&(k=e("
").addClass(v.results).appendTo(x))}},inject:{result:function(e,t,s){f.verbose("Injecting result into results");var n=s!==r?k.children().eq(s).children(b.result).eq(t):k.children(b.result).eq(t);f.verbose("Injecting results metadata",n),n.data(h.result,e)},id:function(t){f.debug("Injecting unique ids into results");var s=0,n=0;return"category"===p.type?e.each(t,function(t,a){n=0,e.each(a.results,function(e,t){var i=a.results[e];i.id===r&&(i.id=f.create.id(n,s)),f.inject.result(i,n,s),n++}),s++}):e.each(t,function(e,s){var a=t[e];a.id===r&&(a.id=f.create.id(n)),f.inject.result(a,n),n++}),t}},save:{results:function(e){f.verbose("Saving current search results to metadata",e),x.data(h.results,e)}},write:{cache:function(e,t){var s=x.data(h.cache)!==r?x.data(h.cache):{};p.cache&&(f.verbose("Writing generated html to cache",e,t),s[e]=t,x.data(h.cache,s))}},addResults:function(t){return e.isFunction(p.onResultsAdd)&&p.onResultsAdd.call(k,t)===!1?(f.debug("onResultsAdd callback cancelled default action"),!1):(k.html(t),void f.showResults())},showResults:function(){f.is.visible()||!f.is.focused()||f.is.empty()||(f.can.transition()?(f.debug("Showing results with css animations"),k.transition({animation:p.transition+" in",debug:p.debug,verbose:p.verbose,duration:p.duration,queue:!0})):(f.debug("Showing results with javascript"),k.stop().fadeIn(p.duration,p.easing)),p.onResultsOpen.call(k))},hideResults:function(){f.is.visible()&&(f.can.transition()?(f.debug("Hiding results with css animations"),k.transition({animation:p.transition+" out",debug:p.debug,verbose:p.verbose,duration:p.duration,queue:!0})):(f.debug("Hiding results with javascript"),k.stop().fadeOut(p.duration,p.easing)),p.onResultsClose.call(k))},generateResults:function(t){f.debug("Generating html from response",t);var s=p.templates[p.type],r=e.isPlainObject(t.results)&&!e.isEmptyObject(t.results),n=e.isArray(t.results)&&t.results.length>0,a="";return r||n?(p.maxResults>0&&(r?"standard"==p.type&&f.error(y.maxResults):t.results=t.results.slice(0,p.maxResults)),e.isFunction(s)?a=s(t):f.error(y.noTemplate,!1)):a=f.displayMessage(y.noResults,"empty"),p.onResults.call(A,t),a},displayMessage:function(e,t){return t=t||"standard",f.debug("Displaying message",e,t),f.addResults(p.templates.message(e,t)),p.templates.message(e,t)},setting:function(t,s){if(e.isPlainObject(t))e.extend(!0,p,t);else{if(s===r)return p[t];p[t]=s}},internal:function(t,s){if(e.isPlainObject(t))e.extend(!0,f,t);else{if(s===r)return f[t];f[t]=s}},debug:function(){p.debug&&(p.performance?f.performance.log(arguments):(f.debug=Function.prototype.bind.call(console.info,console,p.name+":"),f.debug.apply(console,arguments)))},verbose:function(){p.verbose&&p.debug&&(p.performance?f.performance.log(arguments):(f.verbose=Function.prototype.bind.call(console.info,console,p.name+":"),f.verbose.apply(console,arguments)))},error:function(){f.error=Function.prototype.bind.call(console.error,console,p.name+":"),f.error.apply(console,arguments)},performance:{log:function(e){var t,s,r;p.performance&&(t=(new Date).getTime(),r=o||t,s=t-r,o=t,u.push({Name:e[0],Arguments:[].slice.call(e,1)||"",Element:A,"Execution Time":s})),clearTimeout(f.performance.timer),f.performance.timer=setTimeout(f.performance.display,500)},display:function(){var t=p.name+":",s=0;o=!1,clearTimeout(f.performance.timer),e.each(u,function(e,t){s+=t["Execution Time"]}),t+=" "+s+"ms",c&&(t+=" '"+c+"'"),i.length>1&&(t+=" ("+i.length+")"),(console.group!==r||console.table!==r)&&u.length>0&&(console.groupCollapsed(t),console.table?console.table(u):e.each(u,function(e,t){console.log(t.Name+": "+t["Execution Time"]+"ms")}),console.groupEnd()),u=[]}},invoke:function(t,s,n){var i,c,o,u=E;return s=s||g,n=A||n,"string"==typeof t&&u!==r&&(t=t.split(/[\. ]/),i=t.length-1,e.each(t,function(s,n){var a=s!=i?n+t[s+1].charAt(0).toUpperCase()+t[s+1].slice(1):t;if(e.isPlainObject(u[a])&&s!=i)u=u[a];else{if(u[a]!==r)return c=u[a],!1;if(!e.isPlainObject(u[n])||s==i)return u[n]!==r?(c=u[n],!1):!1;u=u[n]}})),e.isFunction(c)?o=c.apply(n,s):c!==r&&(o=c),e.isArray(a)?a.push(o):a!==r?a=[a,o]:o!==r&&(a=o),c}},d?(E===r&&f.initialize(),f.invoke(l)):(E!==r&&E.invoke("destroy"),f.initialize())}),a!==r?a:this},e.fn.search.settings={name:"Search",namespace:"search",debug:!1,verbose:!1,performance:!0,type:"standard",minCharacters:1,apiSettings:!1,source:!1,searchFields:["title","description"],searchFullText:!0,automatic:!0,hideDelay:0,searchDelay:200,maxResults:7,cache:!0,transition:"scale",duration:200,easing:"easeOutExpo",onSelect:!1,onResultsAdd:!1,onSearchQuery:function(){},onResults:function(e){},onResultsOpen:function(){},onResultsClose:function(){},className:{active:"active",empty:"empty",focus:"focus",loading:"loading",results:"results",pressed:"down"},error:{source:"Cannot search. No source used, and Semantic API module was not included",noResults:"Your search returned no results",logging:"Error in debug logging, exiting.",noEndpoint:"No search endpoint was specified",noTemplate:"A valid template name was not specified.",serverError:"There was an issue querying the server.",maxResults:"Results must be an array to use maxResults setting",method:"The method you called is not defined."},metadata:{cache:"cache",results:"results",result:"result"},regExp:{escape:/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,beginsWith:"(?:s|^)"},selector:{prompt:".prompt",searchButton:".search.button",results:".results",category:".category",result:".result",title:".title, .name"},templates:{escape:function(e){var t=/[&<>"'`]/g,s=/[&<>"'`]/,r={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"},n=function(e){return r[e]};return s.test(e)?e.replace(t,n):e},message:function(e,t){var s="";return e!==r&&t!==r&&(s+='
',s+="empty"==t?'
No Results
'+e+'
':'
'+e+"
",s+="
"),s},category:function(t){var s="",n=e.fn.search.settings.templates.escape;return t.results!==r?(e.each(t.results,function(t,a){a.results!==r&&a.results.length>0&&(s+='
'+a.name+"
",e.each(a.results,function(e,t){s+='
',t.url&&(s+=''),t.image!==r&&(t.image=n(t.image),s+='
'),s+='
',t.price!==r&&(t.price=n(t.price),s+='
'+t.price+"
"),t.title!==r&&(t.title=n(t.title),s+='
'+t.title+"
"),t.description!==r&&(s+='
'+t.description+"
"),s+="
"}),s+="
")}),t.action&&(s+=''+t.action.text+""),s):!1},standard:function(t){var s="";return t.results!==r?(e.each(t.results,function(e,t){s+=t.url?'':'',t.image!==r&&(s+='
'),s+='
',t.price!==r&&(s+='
'+t.price+"
"),t.title!==r&&(s+='
'+t.title+"
"),t.description!==r&&(s+='
'+t.description+"
"),s+="
",s+="
"}),t.action&&(s+=''+t.action.text+""),s):!1}}}}(jQuery,window,document); \ No newline at end of file