$re = '/function\(([^)]*)\) {\s+(?:return )?([^;\v]+);\s+}/';
$str = 'goog.provide(\'smb.controllers.SearchboxForDashboards\');
goog.require(\'goog.array\');
goog.require(\'goog.functions\');
goog.require(\'goog.iter\');
goog.require(\'goog.object\');
goog.require(\'goog.string.format\');
goog.require(\'smb.model.Dashboard\');
goog.require(\'smb.model.User\');
goog.scope(function() {
var User = smb.model.User;
/**
* @param {!angular.Scope} $scope Injected Angular scope.
* @param {!angular.Route} $route Injected angular route.
* @param {!angular.Location} $location Injected angular location.
* @param {!angular.Resource} $resource Injected angular resource.
* @param {!smb.services.navigation} navigationService The navigationService.
* @constructor
*/
smb.controllers.SearchboxForDashboards = function($scope, $route, $location,
$resource, navigationService) {
/** @private {!angular.Scope} */
this.scope_ = $scope;
/** @private {!smb.services.navigation} */
this.navigationService_ = navigationService;
/** @private {!angular.Route} */
this.route_ = $route;
/** @private {!angular.Location} */
this.location_ = $location;
/** @private {!angular.Resource} */
this.resource_ = $resource;
/** @private */
this.active_ = this.scope_.active;
/**
* Current search string.
* @export {string}
*/
this.search = \'\';
/**
* The list of current search results.
* @export {!Array<!SearchboxForDashboards.SearchResultStruct>}
*/
this.searchResults = [];
};
var SearchboxForDashboards = smb.controllers.SearchboxForDashboards;
/**
* @return {boolean} Whether there are no search results.
* @private
*/
SearchboxForDashboards.prototype.hasNoSearchResults_ = function() {
return this.searchResults.length == 0;
};
/**
* Types of component contained in a search result.
* @enum {string}
*/
SearchboxForDashboards.ComponentType = {
TAG: \'tag\',
TITLE: \'title\',
DESCRIPTION: \'description\'
};
/**
* FIRST_SUBSEARCH is a multiplier applied for matches starting at the beginning
* of the text as a whole. STARTING_MATCH is applies for matches at the
* beginning of any word.
* @typedef {{
* FIRST_SUBSEARCH: number,
* STARTING_MATCH: number
* }}
*/
SearchboxForDashboards.ComponentWeight;
/**
* Weight settings for the various components, can be tweaked to change results.
* @const {!Object<!SearchboxForDashboards.ComponentType,
* !SearchboxForDashboards.ComponentWeight>}
*/
SearchboxForDashboards.WEIGHTS = goog.object.create(
SearchboxForDashboards.ComponentType.TAG, {
FIRST_SUBSEARCH: 1,
STARTING_MATCH: 2
},
SearchboxForDashboards.ComponentType.TITLE, {
FIRST_SUBSEARCH: 10,
STARTING_MATCH: 10
},
SearchboxForDashboards.ComponentType.DESCRIPTION, {
FIRST_SUBSEARCH: 10,
STARTING_MATCH: 10
}
);
/**
* One feature of the search result.
* @typedef {{
* type: !SearchboxForDashboards.ComponentType,
* match: string,
* tag: (string|undefined),
* title: (string|undefined),
* score: number,
* }}
*/
SearchboxForDashboards.SearchResultComponent;
/**
* Search result with scoring information.
* @typedef {{
* dashboard: !smb.model.Dashboard,
* components: !Array<!SearchResultComponent>,
* bestTitleMatch: ?string,
* bestDescriptionMatch: ?string,
* score: number,
* }}
*/
SearchboxForDashboards.SearchResultStruct;
/**
* Gives a match score for a dashboard.
* TODO(b/27602041) Do more in-depth research into whether there are feasible
* alternatives to writing custom search logic that still give us fine
* control over ranking.
* @param {string} search The text to search.
* @param {!smb.model.Dashboard} dashboard The dashboard to which to apply the
* search.
* @param {boolean=} opt_recur Whether to recursively perform subsearches
* for the different words in the search, i.e "word1 word2 word3" would
* do "word1 word2" and "word2 word3", and then the same recursively.
* @param {boolean=} opt_isFirstSearch Whether this is should be treated as
* matching at the beginning of a search.
* @return {!SearchResultStruct} The result of the search.
* @private
*/
SearchboxForDashboards.prototype.doDashboardSearch_ =
function(search, dashboard, opt_recur, opt_isFirstSearch) {
var isFirstSearch = opt_isFirstSearch;
var struct = {dashboard: dashboard, components: []};
var titleSearchComponent =
this.doTitleSearch_(search, dashboard, isFirstSearch);
if (titleSearchComponent != null) {
struct.components.push(titleSearchComponent);
}
var descriptionSearchComponent =
this.doDescriptionSearch_(search, dashboard, isFirstSearch);
if (descriptionSearchComponent != null) {
struct.components.push(descriptionSearchComponent);
}
var tagSearchComponent = this.doTagSearch_(search, dashboard);
if (tagSearchComponent != null) {
struct.components.push(tagSearchComponent);
}
// now we search for all the n-gram substrings
if (opt_recur) {
var re = /\\s+/;
var tokens = search.split(re);
var len = tokens.length;
for (var runLength = len - 1; runLength > 0; runLength--) {
for (var start = 0; start <= len - runLength; start++) {
var subArr = tokens.slice(start, start + runLength);
var newSearch = subArr.join(\' \');
var isFirstSearch = start == 0;
var subStruct =
this.doDashboardSearch_(newSearch, dashboard, false, isFirstSearch);
struct.components = struct.components.concat(subStruct.components);
}
}
}
struct.components = struct.components.sort(function(a, b) {
return b.score - a.score;
});
var bestTitleMatch = goog.array.find(struct.components, function(component) {
return component.type == SearchboxForDashboards.TITLE;
});
if (bestTitleMatch) {
struct.bestTitleMatch = bestTitleMatch.title;
}
var bestDescriptionMatch = goog.array.find(struct.components,
function(component) {
return component.type == SearchboxForDashboards.DESCRIPTION;
});
if (bestDescriptionMatch) {
struct.bestDescriptionMatch = bestDescriptionMatch.description;
}
this.computeAndSetSearchResultScore_(struct);
return struct;
};
/**
* Computes score for the search result and sets score for the search result.
* @param {!SearchResultStruct} searchResult The search result to operate on.
* @private
*/
SearchboxForDashboards.prototype.computeAndSetSearchResultScore_ =
function(searchResult) {
searchResult.score = searchResult.components.map(function(component) {
return component.score;
}).reduce(function(a, b) { return a + b; }, 0);
};
/**
* Performs search on dashboard title.
* @param {string} search The text to search.
* @param {!smb.model.Dashboard} dashboard The dashboard to perform search on.
* @param {boolean} isFirstSearch Whether to treat this as matching at the
* beginning of a search.
* @return {?SearchboxForDashboards.SearchResultComponent} The search match
* component.
* @private
*/
SearchboxForDashboards.prototype.doTitleSearch_ =
function(search, dashboard, isFirstSearch) {
var title = dashboard.titleDisplayText || dashboard.menuDisplayText;
return this.doTextSearch_(search, title, isFirstSearch,
SearchboxForDashboards.ComponentType.TITLE);
};
/**
* Performs search on dashboard description.
* @param {string} search The text to search.
* @param {!smb.model.Dashboard} dashboard The dashboard to perform search on.
* @param {boolean} isFirstSearch Whether to treat this as matching at the
* beginning of a search.
* @return {?SearchboxForDashboards.SearchResultComponent} The search match
* component.
* @private
*/
SearchboxForDashboards.prototype.doDescriptionSearch_ =
function(search, dashboard, isFirstSearch) {
var title = dashboard.titleDisplayText || dashboard.menuDisplayText;
return this.doTextSearch_(search, title, isFirstSearch,
SearchboxForDashboards.ComponentType.DESCRIPTION);
};
/**
* Searches the text.
* @param {string} search
* @param {string} text
* @param {boolean} isFirstSearch
* @param {smb.controllers.SearchboxForDashboards.ComponentType} type
* @return {?SearchboxForDashboards.SearchResultComponent} The search match
* component.
* @private
*/
SearchboxForDashboards.prototype.doTextSearch_ = function(search, text,
isFirstSearch, type) {
var score = 0;
var searchRegex = new RegExp(search, \'i\');
var match = text.match(searchRegex);
if (!match) {
return null;
}
score = match[0].length;
var weight = SearchboxForDashboards.WEIGHTS[type];
if (text.toLowerCase().startsWith(match[0].toLowerCase())) {
score *= weight.STARTING_MATCH;
if (isFirstSearch) {
score *= weight.FIRST_SUBSEARCH;
}
}
return goog.object.create(
type, match[0],
\'score\', score,
\'type\', type,
\'match\', search
);
};
/**
* Perform search on dashboard tags.
* @param {string} search The text to search.
* @param {!smb.model.Dashboard} dashboard The dashboard to perform search on.
* @param {boolean} isFirstSearch
* @return {?SearchboxForDashboards.SearchResultComponent} The search match
* component.
* @private
*/
SearchboxForDashboards.prototype.doTagSearch_ = function(search, dashboard,
isFirstSearch) {
if (!(\'tags\' in dashboard)) {
return null;
}
var tagMatches = dashboard.tags.map(function(tag) {
return this.doTextSearch_(search, tag, isFirstSearch,
SearchboxForDashboards.ComponentType.TAG);
}, this);
var tagMatches = [];
var groups = goog.iter.groupBy(tagMatches,
function(tagMatch) { return tagMatch.match; });
var scores = [];
var score = 0;
var iterLeft = true;
while (iterLeft) {
try {
var matchTup = groups.next();
var key = matchTup[0];
var matches = matchTup[1];
var numMatches = matches.length;
var scores = matches.map(function(match) { return match.score; })
.reduce(function(a, b) { return a + b; }, 0);
var average = scores / numMatches;
// numMatches + 1 because the score for one should not be zero
// have to do log because too many dups for sqrt to make sense
score += (average * Math.log(numMatches + 1));
} catch (e) {
// goog.iter throws exception at the end rather than having a flag
// to check
iterLeft = false;
}
}
return {
score: score,
type: SearchboxForDashboards.ComponentType.TAG,
match: search
};
};
/**
* @param {string} search The text to search for.
* @param {!Array<!smb.model.Dashboard>} dashboards The dashboards to search
* over.
* @return {!Array<!SearchResultStruct>} The results, sorted.
* @private
*/
SearchboxForDashboards.prototype.doSearch_ = function(search, dashboards) {
var scoreStructs = dashboards.map(function(dashboard) {
return this.doDashboardSearch_(search, dashboard, true, true);
}, this);
return scoreStructs
.filter(function(struct) { return struct.score > 0; }, this)
.sort(function(aStruct, bStruct) {
return bStruct.score - aStruct.score;
});
};
/**
* Updates the autocomplete results, showing the appropriate amount for the
* current search text.
*/
SearchboxForDashboards.prototype.updateAutoComplete = function() {
var dashboards = this.navigationService_.getSearchableDashboards();
if (!dashboards) return;
var searchResults = this.doSearch_(this.search, dashboards);
var searchResultsShow = searchResults.length > 0;
this.searchResults = searchResults;
this.saveAutoCompleteEvent_();
};
/**
* Logs autocomplete search.
* @private
*/
SearchboxForDashboards.prototype.saveAutoCompleteEvent_ = function() {
var urlQuery = this.location_.url();
// @see onlinesales.revenueprograms.sas.discovery.server.services.UserEventProvider
var endpoint = this.getUserEventResource_(\'persist\');
var evnt = {
type: User.Event.SEARCH,
urlQuery: urlQuery,
search: this.search,
session: smbuser.session
};
endpoint.save(evnt);
};
/**
* Logs search result click.
* @param {number} dashboardId the id of the relavant dashboard
* @private
*/
SearchboxForDashboards.prototype.saveSearchResultClick_ = function(dashboardId) {
var urlQuery = this.location_.url();
// @see onlinesales.revenueprograms.sas.discovery.server.services.UserEventProvider
var endpoint = this.getUserEventResource_(\'persist\');
var evnt = {
dashboardId: dashboardId,
type: User.Event.SEARCH_CLICK,
urlQuery: urlQuery,
search: this.search,
session: smbuser.session
};
endpoint.save(evnt);
};
/**
* @param {string} action The action path for which to get endpoint.
* @return {!ngResource} Angular resource for the action on user events.
* @private
*/
SearchboxForDashboards.prototype.getUserEventResource_ = function(action) {
return this.resource_(
goog.string.format(\'%s/%s\', User.EVENT_ENDPOINT, action));
};
/**
* Currently returns the arguments, here because of past and potentially
* future formatting.
* @param {string} tag The tag string.
* @return {string} the tag display text
*/
SearchboxForDashboards.prototype.getTagDisplayText = goog.functions.identity;
/**
* Gets the class for the given tag.
* @param {string} tag The tag.
* @return {string} The class name.
*/
SearchboxForDashboards.prototype.getTagDisplayClass = function(tag) {
if (tag) {
var strArr = tag.split(\':\');
if (strArr.length == 2) {
return goog.string.format(\'tags-style-%s\', strArr[0]);
} else {
return \'\';
}
}
return \'\';
};
/**
* Goes to the search results page, if that feature is enabled.
*
* @param {!jQuery.Event|!JQLite.Event} evt The fired event on keypress.
*
* @export
*/
SearchboxForDashboards.prototype.searchBoxKeypressed = function(evt) {
};
/**
* Handler for clicking search result item. Navigates to the dashboard.
* @param {!smb.model.Dashboard} dashboard The dashboard selection clicked.
* @param {!jQuery.Event|!JQLite.Event} $event The event for the click.
*/
SearchboxForDashboards.prototype.resultItemClicked = function(dashboard, $event) {
$event.stopPropagation();
this.saveSearchResultClick_(dashboard.id);
this.navigateToDashboard_(dashboard);
};
/**
* Navigates to the given dashboard.
* @param {!smb.model.Dashboard} dashboard The dashboard to navigate to.
* @private
*/
SearchboxForDashboards.prototype.navigateToDashboard_ = function(dashboard) {
this.reset_();
this.location_.search({});
this.location_.path(dashboard.navigationURLPath);
this.route_.reload();
};
/**
* @param {!SearchboxForDashboards.SearchResultStruct} resultObj Representation of the
* search result.
* @return {?string} The title text for the search result, if there is any.
*/
SearchboxForDashboards.prototype.getTitleText = function(resultObj) {
var dash = resultObj.dashboard;
return dash.titleDisplayText || dash.menuDisplayText;
};
/**
* @param {!SearchboxForDashboards.SearchResultStruct} resultObj Representation of the
* search result.
* @return {string} The description text for the search result.
*/
SearchboxForDashboards.prototype.getDescriptionText = function(resultObj) {
return resultObj.dashboard.flatDescription;
};
/**
* @return {boolean} Whether to show the search box on this page.
*/
SearchboxForDashboards.prototype.shouldShowResultBox = function() {
// Shows when it is active, there are results, and there is a search
return this.scope_.active && !this.hasNoSearchResults_() &&
this.search.length > 0;
};
/**
* Sets the search text to the empty string.
* @private
*/
SearchboxForDashboards.prototype.clearSearchBoxText_ = function() {
this.search = \'\';
};
/**
* Sets the search results to an empty array.
* @private
*/
SearchboxForDashboards.prototype.clearSearchResults_ = function() {
this.searchResults = [];
};
/**
* Reverts to initial state.
* @private
*/
SearchboxForDashboards.prototype.reset_ = function() {
this.clearSearchBoxText_();
this.clearSearchResults_();
};
}); // goog.scope
';
$subst = "($1) => $2";
$result = preg_replace($re, $subst, $str);
echo "The result of the substitution is ".$result;
Please keep in mind that these code samples are automatically generated and are not guaranteed to work. If you find any syntax errors, feel free to submit a bug report. For a full regex reference for PHP, please visit: http://php.net/manual/en/ref.pcre.php