Last active
September 20, 2016 03:54
-
-
Save artchen/a24102f119cf67e385db7cfa39a9c812 to your computer and use it in GitHub Desktop.
Universal Search Common Logic
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var SearchService = ""; | |
(function($) { | |
/** | |
* A super class of common logics for all search services | |
* @param options : (object) | |
*/ | |
SearchService = function(options) { | |
var self = this; | |
self.config = $.extend({ | |
per_page: 10, | |
selectors: { | |
body: "body", | |
form: ".u-search-form", | |
input: ".u-search-input", | |
container: "#u-search", | |
modal: "#u-search .modal", | |
modal_body: "#u-search .modal-body", | |
modal_footer: "#u-search .modal-footer", | |
modal_overlay: "#u-search .modal-overlay", | |
modal_results: "#u-search .modal-results", | |
modal_metadata: "#u-search .modal-metadata", | |
modal_error: "#u-search .modal-error", | |
modal_loading_bar: "#u-search .modal-loading-bar", | |
modal_ajax_content: "#u-search .modal-ajax-content", | |
modal_logo: '#u-search .modal-footer .logo', | |
btn_close: "#u-search .btn-close", | |
btn_next: "#u-search .btn-next", | |
btn_prev: "#u-search .btn-prev" | |
}, | |
brands: { | |
'google': {logo: '/img/google.svg', url: 'https://cse.google.com'}, | |
'algolia': {logo: '/img/algolia.svg', url: 'https://www.algolia.com'}, | |
'hexo': {logo: '', url: ''}, | |
'azure': {logo: '/img/azure.svg', url: 'https://azure.microsoft.com/en-us/services/search/'}, | |
'baidu': {logo: '/img/baidu.svg', url: 'http://zn.baidu.com/cse/home/index'} | |
} | |
}, options); | |
self.dom = {}; | |
self.percentLoaded = 0; | |
self.open = false; | |
self.queryText = ""; | |
self.nav = { | |
next: -1, | |
prev: -1, | |
total: 0, | |
current: 1 | |
}; | |
self.parseSelectors = function() { | |
for (var key in self.config.selectors) { | |
self.dom[key] = $(self.config.selectors[key]); | |
} | |
}; | |
self.beforeQuery = function() { | |
if (!self.open) { | |
self.dom.container.fadeIn(); | |
self.dom.body.addClass('modal-active'); | |
} | |
self.dom.input.each(function(index,elem) { | |
$(elem).val(self.queryText); | |
}); | |
document.activeElement.blur(); | |
self.dom.modal_error.hide(); | |
self.dom.modal_ajax_content.removeClass('loaded'); | |
self.startLoading(); | |
}; | |
self.afterQuery = function() { | |
self.dom.modal_body.scrollTop(0); | |
self.dom.modal_ajax_content.addClass('loaded'); | |
self.stopLoading(); | |
}; | |
/** | |
* Perform a complete serach operation including UI updates and query | |
* @param startIndex {int} start index or page number | |
*/ | |
self.search = function(startIndex, callback) { | |
self.beforeQuery(); | |
if (self.search instanceof Function) { | |
self.query(self.queryText, startIndex, function() { | |
self.afterQuery(); | |
}); | |
} | |
else { | |
console.log("query() does not exist."); | |
self.onQueryError(self.queryText, ''); | |
self.afterQuery(); | |
} | |
}; | |
/** | |
* Query error handler | |
* @param queryText: (string) | |
* @param status: (string) | |
*/ | |
self.onQueryError = function(queryText, status) { | |
var errMsg = ""; | |
if (status === "success") errMsg = "No result found for \"" +queryText+ "\"."; | |
else if (status === "timeout") errMsg = "Unfortunate timeout."; | |
else errMsg = "Mysterious failure."; | |
self.dom.modal_results.html(""); | |
self.dom.modal_error.html(errMsg); | |
self.dom.modal_error.show(); | |
}; | |
self.nextPage = function() { | |
if (self.nav.next !== -1) { | |
self.search(self.nav.next); | |
} | |
}; | |
self.prevPage = function() { | |
if (self.nav.prev !== -1) { | |
self.search(self.nav.prev); | |
} | |
}; | |
/** | |
* Generate html for one result | |
* @param url : (string) url | |
* @param title : (string) title | |
* @param digest : (string) digest | |
*/ | |
self.buildResult = function(url, title, digest) { | |
var html = ""; | |
html = "<li>"; | |
html += "<a class='result' href='" +url+ "'>"; | |
html += "<span class='title'>" +title+ "</span>"; | |
html += "<span class='digest'>" +digest+ "</span>"; | |
html += "<span class='icon icon-chevron-thin-right'></span>"; | |
html += "</a>"; | |
html += "</li>"; | |
return html; | |
}; | |
/** | |
* Close the modal, resume body scrolling | |
* no param | |
*/ | |
self.close = function() { | |
self.open = false; | |
self.dom.container.fadeOut(); | |
self.dom.body.removeClass('modal-active'); | |
}; | |
/** | |
* Searchform submit event handler | |
* @param queryText : (string) the query text | |
*/ | |
self.onSubmit = function(event) { | |
event.preventDefault(); | |
self.queryText = $(this).find('.u-search-input').val(); | |
if (self.queryText) { | |
self.search(1); | |
} | |
}; | |
/** | |
* Start loading bar animation | |
* no param | |
*/ | |
self.startLoading = function() { | |
self.dom.modal_loading_bar.show(); | |
self.loadingTimer = setInterval(function() { | |
self.percentLoaded = Math.min(self.percentLoaded+5,95); | |
self.dom.modal_loading_bar.css('width', self.percentLoaded+'%'); | |
}, 100); | |
}; | |
/** | |
* Stop loading bar animation | |
* no param | |
*/ | |
self.stopLoading = function() { | |
clearInterval(self.loadingTimer); | |
self.dom.modal_loading_bar.css('width', '100%'); | |
self.dom.modal_loading_bar.fadeOut(); | |
setTimeout(function() { | |
self.percentLoaded = 0; | |
self.dom.modal_loading_bar.css('width', '0%'); | |
}, 300); | |
}; | |
/** | |
* Add service branding | |
* @param service {String} service name | |
*/ | |
self.addLogo = function(service) { | |
var html = ""; | |
if (self.config.brands[service] && self.config.brands[service].logo) { | |
html += "<a href='" +self.config.brands[service].url+ "' class='" +service+ "'>"; | |
html += '<img src="' +self.config.brands[service].logo+ '" />'; | |
html += "</a>"; | |
self.dom.modal_logo.html(html); | |
} | |
}; | |
self.destroy = function() { | |
self.dom.form.each(function(index,elem) { | |
$(elem).off('submit'); | |
}); | |
self.dom.modal_overlay.off('click'); | |
self.dom.btn_close.off('click'); | |
self.dom.btn_next.off('click'); | |
self.dom.btn_prev.off('click'); | |
self.dom.container.remove(); | |
}; | |
/** | |
* Load template and register event handlers | |
* no param | |
*/ | |
self.init = function() { | |
$('body').append(template); | |
self.parseSelectors(); | |
self.dom.form.each(function(index,elem) { | |
$(elem).on('submit', self.onSubmit); | |
}); | |
self.dom.modal_overlay.on('click', self.close); | |
self.dom.btn_close.on('click', self.close); | |
self.dom.btn_next.on('click', self.nextPage); | |
self.dom.btn_prev.on('click', self.prevPage); | |
}; | |
self.init(); | |
return self; | |
}; | |
var template = '<div id="u-search"><div class="modal"> <header class="modal-header" class="clearfix"><form id="u-search-modal-form" class="u-search-form" name="uSearchModalForm"> <input type="text" id="u-search-modal-input" class="u-search-input" /> <button type="submit" id="u-search-modal-btn-submit" class="u-search-btn-submit"> <span class="icon icon-search"></span> </button></form> <a class="btn-close"> <span class="icon icon-close"></span> </a><div class="modal-loading"><div class="modal-loading-bar"></div></div> </header> <main class="modal-body"><ul class="modal-results modal-ajax-content"></ul> </main> <footer class="modal-footer clearfix"><div class="modal-metadata modal-ajax-content"> <strong class="range"></strong> of <strong class="total"></strong></div><div class="modal-error"></div> <div class="logo"></div> <a class="nav btn-next modal-ajax-content"> <span class="text">NEXT</span> <span class="icon icon-chevron-right"></span> </a> <a class="nav btn-prev modal-ajax-content"> <span class="icon icon-chevron-left"></span> <span class="text">PREV</span> </a> </footer></div><div class="modal-overlay"></div></div>'; | |
})(jQuery); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment