// // Alfresco OpenSearch client library // Gavin Cornwell 09-01-2007 // // NOTE: This script relies on common.js so therefore needs to be loaded // prior to this one on the containing HTML page. var _OS_NS_PREFIX = "opensearch"; var _OS_NS_URI = "http://a9.com/-/spec/opensearch/1.1/"; var _RESULTS_DIV_ID_SUFFIX = "-os-results"; var _OPTIONS_DIV_ID_SUFFIX = "-os-options"; var _RESULTSET_PANEL_DIV_ID_SUFFIX = "-osresults-panel"; var _RESULTSET_LIST_DIV_ID_SUFFIX = "-osresults-list"; var _RESULTSET_PAGING_DIV_ID_SUFFIX = "-osresults-paging"; var _ENGINE_ENABLED_FIELD_ID = "-engine-enabled"; var _SEARCH_TERM_FIELD_ID = "-search-term"; var _PAGE_SIZE_FIELD_ID = "-page-size"; /** * Constructor for an object to hold the definition of an OpenSearch engine */ Alfresco.OpenSearchEngine = function(id, label, url) { this.id = id; this.label = label; this.url = url; } /** * Constructor for an OpenSearchClient object */ Alfresco.OpenSearchClient = function(id) { this.id = id; this.engines = []; this.enginesById = []; } Alfresco.OpenSearchClient.prototype = { id: null, engines: null, enginesById: null, /** * Registers an OpenSearch engine to be called when performing queries */ registerOpenSearchEngine: function(id, label, url) { var se = new Alfresco.OpenSearchEngine(id, label, url); this.engines[this.engines.length] = se; this.enginesById[id] = se; }, /** * Handles the key press event, if ENTER is pressed execute the query */ handleKeyPress: function(e) { var keycode; // get the keycode if (window.event) { keycode = window.event.keyCode; } else if (e) { keycode = e.which; } // if ENTER was pressed execute the query if (keycode == 13) { this.executeQuery(); return false; } return true; }, /** * Toggles the visibility of the options panel */ toggleOptions: function(icon) { var currentState = icon.className; var optionsDiv = document.getElementById(this.id + _OPTIONS_DIV_ID_SUFFIX); if (currentState == "collapsed") { icon.src = getContextPath() + "/images/icons/expanded.gif"; icon.className = "expanded"; // show the div holding the options if (optionsDiv != null) { optionsDiv.style.display = "block"; } } else { icon.src = getContextPath() + "/images/icons/collapsed.gif"; icon.className = "collapsed"; // hide the div holding the options if (optionsDiv != null) { optionsDiv.style.display = "none"; } } }, /** * Executes the query against all the registered and selected opensearch engines */ executeQuery: function() { // gather the required parameters var term = document.getElementById(this.id + _SEARCH_TERM_FIELD_ID).value; var count = document.getElementById(this.id + _PAGE_SIZE_FIELD_ID).value; // default the count if its invalid if (count.length == 0 || isNaN(count)) { count = 5; } // issue the queries if there is enough search criteria if (term != null && term.length > 1) { // remove previous results (if necessary) var resultsPanel = document.getElementById(this.id + _RESULTS_DIV_ID_SUFFIX); if (resultsPanel != null) { while (resultsPanel.firstChild) { resultsPanel.removeChild(resultsPanel.firstChild); }; } // issue the search request for each enabled engine for (var e = 0; e < this.engines.length; e++) { // get the checkbox for the current engine var ose = this.engines[e]; var engCheckbox = document.getElementById(this.id + "-" + ose.id + _ENGINE_ENABLED_FIELD_ID); if (engCheckbox != null && engCheckbox.checked) { this.issueSearchRequest(ose, term, count); } } } }, /** * Issues an Ajax request for the given OpenSearchEngine * using the given search term and page size. */ issueSearchRequest: function(ose, term, pageSize) { // generate the search url var searchUrl = this.calculateSearchUrl(ose.url, term, pageSize); // issue the request if (searchUrl != null) { YAHOO.util.Connect.asyncRequest("GET", searchUrl, { success: Alfresco.processSearchResults, failure: Alfresco.handleSearchError, argument: [ose.id, this] }, null); } else { handleErrorYahoo("Failed to generate url for search engine '" + ose.label + "'.\n\nThis is probably caused by missing required parameters, check the template url for the search engine."); } }, /** * Shows another page of the current search results for the * given engineId */ showPage: function(engineId, url) { // execute the query and process the results YAHOO.util.Connect.asyncRequest("GET", url, { success: Alfresco.processShowPageResults, failure: Alfresco.handleSearchError, argument: [engineId, this] }, null); }, /** * Generates a concrete url for the given template url and parameters. * * All parameters (inside { and }) have to be replaced. We only need to populate * the 'searchTerms' and 'count' parameters, all optional ones will use the * empty string. If there is a mandatory parameter present (other than searchTerms * and count) null will be returned. */ calculateSearchUrl: function(templateUrl, term, count) { var searchUrl = null; // define regex pattern to look for params var pattern = /\{+\w*\}+|\{+\w*\?\}+|\{+\w*:\w*\}+|\{+\w*:\w*\?\}+/g; var params = templateUrl.match(pattern); if (params != null && params.length > 0) { searchUrl = templateUrl; // go through the parameters and replace the searchTerms and count // parameters with the given values and then replace all optional // parameters with an empty string. for (var p = 0; p < params.length; p++) { var param = params[p]; if (param == "{searchTerms}") { searchUrl = searchUrl.replace(param, term); } else if (param == "{count}" || param == "{count?}") { searchUrl = searchUrl.replace(param, count); } else if (param.indexOf("?") != -1) { // replace the optional parameter with "" searchUrl = searchUrl.replace(param, ""); } else { // an unknown manadatory parameter return searchUrl = null; break; } } } return searchUrl; }, /** * Renders the results for the given feed element. */ renderSearchResults: function(engineId, feed) { // look up the label from the osengine registry var engineLabel = this.enginesById[engineId].label; // create the div to hold the results and the header bar var sb = []; sb[sb.length] = "
"; sb[sb.length] = "
"; sb[sb.length] = engineLabel; sb[sb.length] = "
"; // create the actual results to display, start with the containing div sb[sb.length] = "
"; sb[sb.length] = this.generateResultsListHTML(feed); sb[sb.length] = "
"; // create the paging controls sb[sb.length] = "
"; sb[sb.length] = this.generatePagingHTML(engineId, feed); sb[sb.length] = "
"; // close the containing div sb[sb.length] = "
"; // create a div element to hold the results var d = document.createElement("div"); d.innerHTML = sb.join(""); // return the div return d; }, /** * Generates the HTML to display the search results from the * given feed. */ generateResultsListHTML: function(feed) { var isAtom = true; // if the name of the feed element is "channel" this is an RSS feed if (feed.tagName == "channel") { isAtom = false; } var results = null; if (isAtom) { results = feed.getElementsByTagName("entry"); } else { results = feed.getElementsByTagName("item"); } if (results == null || results.length == 0) { return "
No results
"; } else { var sb = []; sb[sb.length] = ""; for (var x = 0; x < results.length; x++) { // get the current entry var elResult = results[x]; // get the title, icon and summary var title = getElementTextByTagName(elResult, "title"); var icon = getElementTextByTagName(elResult, "icon"); var summary = null; if (isAtom) { summary = getElementTextByTagName(elResult, "summary"); } else { summary = getElementTextByTagName(elResult, "description"); } // get the link href var link = null; var elLink = getElementByTagName(elResult, "link"); if (elLink != null) { if (isAtom) { link = elLink.getAttribute("href"); } else { link = getElementText(elLink); } } // generate the row to represent the result sb[sb.length] = ""; } // close the table sb[sb.length] = "
"; if (icon != null) { sb[sb.length] = ""; } sb[sb.length] = "
"; if (title != null) { if (link != null) { sb[sb.length] = ""; } sb[sb.length] = title; if (link != null) { sb[sb.length] = ""; } } sb[sb.length] = "
"; if (summary != null) { sb[sb.length] = summary; } sb[sb.length] = "
"; return sb.join(""); } }, /** * Generates the HTML to display the paging information i.e. the first, next, previous * and last buttons and the position info i.e. "x - y of z". */ generatePagingHTML: function(engineId, feed) { var totalResults = 0; var pageSize = 5; var startIndex = 0; // check there are results var elTotalResults = getElementByTagNameNS(feed, _OS_NS_URI, _OS_NS_PREFIX, "totalResults"); if (elTotalResults != null) { totalResults = getElementText(elTotalResults); } // if there are no results return an empty string if (totalResults == 0) { return ""; } var elStartIndex = getElementByTagNameNS(feed, _OS_NS_URI, _OS_NS_PREFIX, "startIndex"); if (elStartIndex != null) { startIndex = getElementText(elStartIndex); } var elItemsPerPage = getElementByTagNameNS(feed, _OS_NS_URI, _OS_NS_PREFIX, "itemsPerPage"); if (elItemsPerPage != null) { pageSize = getElementText(elItemsPerPage); } // calculate the number of pages the results span /*var noPages = Math.floor(totalResults / pageSize); var remainder = totalResults % pageSize; if (remainder != 0) { noPages++; }*/ var endIndex = (Number(startIndex) + Number(pageSize)) - 1; if (endIndex > totalResults) { endIndex = totalResults; } // extract the navigation urls var firstUrl = null; var nextUrl = null; var previousUrl = null; var lastUrl = null; var links = feed.getElementsByTagName("link"); if (links != null && links.length > 0) { for (var x = 0; x < links.length; x++) { var elNavLink = links[x]; var linkType = elNavLink.getAttribute("rel"); if (linkType == "first") { firstUrl = elNavLink.getAttribute("href"); } else if (linkType == "next") { nextUrl = elNavLink.getAttribute("href"); } else if (linkType == "previous") { previousUrl = elNavLink.getAttribute("href"); } else if (linkType == "last") { lastUrl = elNavLink.getAttribute("href"); } } } var sb = []; if (firstUrl != null) { sb[sb.length] = ""; } else { sb[sb.length] = ""; } sb[sb.length] = " "; if (previousUrl != null) { sb[sb.length] = ""; } else { sb[sb.length] = ""; } sb[sb.length] = "  "; sb[sb.length] = startIndex; sb[sb.length] = " - "; sb[sb.length] = endIndex; sb[sb.length] = " of "; sb[sb.length] = totalResults; sb[sb.length] = "  "; if (nextUrl != null) { sb[sb.length] = ""; } else { sb[sb.length] = ""; } sb[sb.length] = " "; if (lastUrl != null) { sb[sb.length] = ""; } else { sb[sb.length] = ""; } return sb.join(""); } } /*********************************/ /** Handlers for AJAX callbacks **/ /*********************************/ /** * Processes the XML search results */ Alfresco.processSearchResults = function(ajaxResponse) { try { // render the results from the Ajax response var engineId = ajaxResponse.argument[0]; var clientInstance = ajaxResponse.argument[1]; var feed = ajaxResponse.responseXML.documentElement; // if the name of the feed element is "rss", get the channel child element if (feed.tagName == "rss") { feed = getElementByTagName(feed, "channel"); } var resultsDiv = clientInstance.renderSearchResults(engineId, feed); // create the div to hold the results and add the results var resultsPanel = document.getElementById(clientInstance.id + _RESULTS_DIV_ID_SUFFIX); if (resultsPanel != null) { // add the new results resultsPanel.appendChild(resultsDiv); } else { alert("Failed to final results panel, unable to render search results!"); return; } } catch (e) { handleCaughtError(e); } } /** * Processes the search results and updates the postion, result list * and paging controls. */ Alfresco.processShowPageResults = function(ajaxResponse) { try { // render the results from the Ajax response var engineId = ajaxResponse.argument[0]; var clientInstance = ajaxResponse.argument[1]; var feed = ajaxResponse.responseXML.documentElement; // if the name of the feed element is "rss", get the channel child element if (feed.tagName == "rss") { feed = getElementByTagName(feed, "channel"); } // append the results list to the results list div var resultsListDiv = document.getElementById(clientInstance.id + "-" + engineId + _RESULTSET_LIST_DIV_ID_SUFFIX); if (resultsListDiv != null) { resultsListDiv.innerHTML = clientInstance.generateResultsListHTML(feed); } // update the paging div with new urls and position info var pagingDiv = document.getElementById(clientInstance.id + "-" + engineId + _RESULTSET_PAGING_DIV_ID_SUFFIX); if (pagingDiv != null) { pagingDiv.innerHTML = clientInstance.generatePagingHTML(engineId, feed); } } catch (e) { handleCaughtError(e); } } /** * Error handler for Ajax call to search engine */ Alfresco.handleSearchError = function(ajaxResponse) { var engineId = ajaxResponse.argument[0]; var clientInstance = ajaxResponse.argument[1]; var engineLabel = clientInstance.enginesById[engineId].label; handleErrorYahoo("Failed to retrieve search results for '" + engineLabel + "': " + ajaxResponse.status + " " + ajaxResponse.statusText); }