/* Jappix - An open social platform These are the seach tools JS script for Jappix ------------------------------------------------- License: AGPL Author: Valérian Saliou */ // Bundle var Search = (function () { /** * Alias of this * @private */ var self = {}; /* Variables */ self.search_filtered = false; /** * Searches in the user's buddy list * @public * @param {string} query * @return {object} */ self.processBuddy = function(query) { try { // No query submitted? if(!query) { return; } // Wildcard (*) submitted if(query == '*') { query = ''; } // Replace forbidden characters in regex query = Common.escapeRegex(query); // Create an empty array var results = []; // Search regex var regex = new RegExp('((^)|( ))' + query, 'gi'); // Search in the roster var buddies = Roster.getAllBuddies(); for(var i in buddies) { var xid = buddies[i]; var nick = Name.getBuddy(xid); // Buddy match our search, and not yet in the array if(nick.match(regex) && !Utils.existArrayValue(results, xid)) { results.push(xid); } } // Return the results array return results; } catch(e) { Console.error('Search.processBuddy', e); } }; /** * Resets the buddy search tool * @public * @param {string} destination * @return {undefined} */ self.resetBuddy = function(destination) { try { $(destination + ' ul').remove(); $(destination + ' input').removeClass('suggested'); } catch(e) { Console.error('Search.resetBuddy', e); } }; /** * Add the clicked XID to the input * @public * @param {string} destination * @param {string} xid * @return {boolean} */ self.addBuddy = function(destination, xid) { try { // Remove the search tool self.resetBuddy(destination); // Define a selector var input = $(destination + ' input'); var value = input.val(); // Get the old value (if there's another value) var old = ''; if(value.match(/(^(.+)(,)(\s)?)(\w+)$/)) old = RegExp.$1; // Add the XID to the "to" input and focus on it $(document).oneTime(10, function() { input.val(old + xid).focus(); }); } catch(e) { Console.error('Search.addBuddy', e); } finally { return false; } }; /** * Creates the appropriate markup for the search results * @public * @param {string} destination * @return {undefined} */ self.createBuddy = function(destination) { try { // Reset the search engine self.resetBuddy(destination); // Get the entered value var value = $(destination + ' input').val(); // Separation with a comma? if(value.match(/^(.+)((,)(\s)?)(\w+)$/)) value = RegExp.$5; // Get the result array var entered = self.processBuddy(value); // Display each result (if any) if(entered && entered.length) { // Set a special class to the search input $(destination + ' input').addClass('suggested'); // Append each found buddy in the container var regex = new RegExp('((^)|( ))' + value, 'gi'); // Initialize the code generation var code = ''; // Creates the code in the DOM $(destination).append(code); // Put the hover on the first element $(destination + ' ul li:first').addClass('hovered'); // Hover event, to not to remove this onblur and loose the click event $(destination + ' ul li').hover(function() { $(destination + ' ul li').removeClass('hovered'); $(this).addClass('hovered'); // Add a marker for the blur event $(destination + ' ul').attr('mouse-hover', 'true'); }, function() { $(this).removeClass('hovered'); // Remove the mouse over marker $(destination + ' ul').removeAttr('mouse-hover'); }); } } catch(e) { Console.error('Search.createBuddy', e); } }; /** * Handles the keyboard arrows press when searching * @public * @param {object} evt * @param {string} destination * @return {boolean} */ self.arrowsBuddy = function(evt, destination) { try { // Down arrow: 40 // Up arrown: 38 // Initialize var code = evt.keyCode; // Not the key we want here if((code != 40) && (code != 38)) return; // Remove the eventual mouse hover marker $(destination + ' ul').removeAttr('mouse-hover'); // Create the path & get its size var path = destination + ' ul li'; var pSize = $(path).size(); // Define the i value var i = 0; // Switching yet launched if(Common.exists(path + '.hovered')) { var index = $(path).attr('data-hovered'); if(index) i = parseInt(index); if(code == 40) i++; else i--; } else if(code == 38) i = pSize - 1; // We must not override the maximum i limit if(i >= pSize) i = 0; // We must not have negative i else if(i < 0) i = pSize - 1; // Modify the list $(path + '.hovered').removeClass('hovered'); $(path).eq(i).addClass('hovered'); // Store the i index $(path).attr('data-hovered', i); } catch(e) { Console.error('Search.arrowsBuddy', e); } finally { return false; } }; /** * Filters the buddies in the roster * @public * @param {object} vFilter * @return {undefined} */ self.goFilterBuddy = function(vFilter) { try { // Put a marker self.search_filtered = true; // Show the buddies that match the search string var rFilter = self.processBuddy(vFilter); // Hide all the buddies $('#roster .buddy').hide(); // Only show the buddies which match the search if(!Roster.blist_all) { for(var i in rFilter) $('#roster .buddy[data-xid="' + escape(rFilter[i]) + '"]:not(.hidden-buddy)').show(); } else { for(var j in rFilter) $('#roster .buddy[data-xid="' + escape(rFilter[j]) + '"]').show(); } } catch(e) { Console.error('Search.goFilterBuddy', e); } }; /** * Resets the buddy filtering in the roster * @public * @return {undefined} */ self.resetFilterBuddy = function() { try { // Remove the marker self.search_filtered = false; // Show all the buddies $('#roster .buddy').show(); // Only show available buddies if(!Roster.blist_all) $('#roster .buddy.hidden-buddy').hide(); // Update the groups Roster.updateGroups(); } catch(e) { Console.error('Search.resetFilterBuddy', e); } }; /** * Funnels the buddy filtering * @public * @param {number} keycode * @return {undefined} */ self.funnelFilterBuddy = function(keycode) { try { // Get the input value var input = $('#roster .filter input'); var cancel = $('#roster .filter a'); var value = input.val(); // Security: reset all the groups, empty or not, deployed or not $('#roster .one-group, #roster .group-buddies').show(); $('#roster .group span').text('-'); // Nothing is entered, or escape pressed if(!value || (keycode == 27)) { if(keycode == 27) input.val(''); self.resetFilterBuddy(); cancel.hide(); } // Process the filtering else { cancel.show(); self.goFilterBuddy(value); } // Update the groups Roster.updateGroups(); } catch(e) { Console.error('Search.funnelFilterBuddy', e); } }; /** * Searches for the nearest element (with a lower stamp than the current one) * @public * @param {number} stamp * @param {string} element * @return {number} */ self.sortElementByStamp = function(stamp, element) { try { var array = []; var i = 0; var nearest = 0; // Add the stamp values to the array $(element).each(function() { var current_stamp = parseInt($(this).attr('data-stamp')); // Push it! array.push(current_stamp); }); // Sort the array array.sort(); // Get the nearest stamp value while(stamp > array[i]) { nearest = array[i]; i++; } return nearest; } catch(e) { Console.error('Search.sortElementByStamp', e); } }; /** * Return class scope */ return self; })();