/** * @module "biojs-im-list-analysis" * @version 0.0.1 * @author Alex Kalderimis * @license LGPL */ (function(biojs) { var ListWidgets = require('list-widgets'); var module = (biojs.InterMine || (biojs.InterMine = {})); module.ListAnalysis = ListAnalysis; /** Match strings that look like element ids, and not query selectors. **/ var ELEMENT_ID = /^[a-z0-9_-]+$/i; /** * Click handlers that receive an identifier and a type, identifying a single entity. * @callback * @param {string} identifier The public unique identifier of this object, eg: "FGBN01234" * @param {string} type The type of the entity, eg: "Gene" */ /** * Click handlers that receive a definition of a result set as a query. * @callback * @param {InterMineQuery} query The definition of the result set as a query. */ /** * @typedef {Object} InterMineQuery * @property {string} [name=null] The name, for debugging. * @property {string[]} view The selected columns. * @property {Object[]} constraints The query constraints. * @property {Object[]} sortOrder The sort order elements. * @property {Object} joins The join information. * @property {string} constraintLogic The way to combine the constraints. */ /** * typedef {function} WidgetLoader * @param {string} toolName The tool to use. * @param {string} listName The list to analyse. * @param {string|HTMLElement} element The place to render the tool. * @param {Object} options Optional parameters. */ /** * The InterMine Service type. * @external intermine.Service * @see {@link http://intermine.github.io/imjs} */ /** * @typedef {Object} ListWidgets * @property {intermine.Service} imjs The connected service. * @property {WidgetLoader} chart Load a chart. * @property {WidgetLoader} enrichment Load enrichment information. */ /** @class * @name Biojs.InterMine.ListAnalysis */ /** * InterMine.ListAnalysis - Analyse a Set of Items Stored as an InterMine List * =========================================================================== * * This component wraps the functionality of the InterMine * apps-c library * as a BioJS component. It allows users to analyse groups of objects in * aggregate, through statistical analysis, graphical visualisation or * simple table display. * * @name Biojs.InterMine.ListAnalyis * @constructor * * @requires apps-c 2.0.4 * * @dependency * @dependency * * @param {Object} options The options. * * @option {string|HTMLElement|jQuery} target * The place in the DOM to load the table. The value of this can be anything * you can pass to the jQuery selector. It must resolve to one and only one element. * If the target is a string and contains no whitespace or css selector * characters (., #, >, etc), * it will be interpreted as an element id. * @option {string} [url] * The url of the InterMine compatible service to connect to. If not provided, then the "service" * option MUST be provided. * @option {string} [token] * The token of a registered InterMine user. Providing this enables certain features, such * as list creation. Should only be provided if the url option is provided. * @option [service] * Information defining the service. * @option {string} [service.root] * The url of the InterMine compatible service to connect to. * @option {string} [service.token] * The token of a registered InterMine user. * @option {string} tool * The analysis tool to use. This must me a string of the form TYPE:NAME, where * TYPE is one of the known widget types (chart, * enrichment, table), and NAME is one of the * tools registered in the mine. The list of available tools can be obtained by * calls to intermine.Service#fetchWidgets. * @option {string} list * The name of a list to analyse. This must be a list the user has access to. * * @example * var widget = new Biojs.InterMine.ListAnalyis({ * target: 'some-element-id', * url: 'http://www.flymine.org/query/service/', * tool: 'enrichment:pathway_enrichment', * list: 'PL FlyTF_putativeTFs' * }); * */ function ListAnalysis(options) { var widgets, parts, toolType, toolName, self = this; check(options, "No options supplied"); this.element = resolveTarget(options.target); check(this.element, "Could not find " + options.target); check(options.url, "No URL supplied"); check(options.tool, "No tool id supplied"); check(options.list, "No list name supplied"); parts = options.tool.split(':'); check(parts.length === 2, "Tool id must be of the form {type}:{name}."); toolType = parts[0]; toolName = parts[1]; check(toolName.length, "Tool name not supplied"); this._cbs = {}; this._globalListeners = []; var config = Object.create(options.config || {}); config.matchCb = this.fireEvent.bind(this,"onClickMatch"); config.resultsCb = this.fireEvent.bind(this, "onClickViewResults"); config.listCb = this.fireEvent.bind(this, "onClickCreateList"); self.widgets = widgets = new ListWidgets({root: options.url, token: options.token}); check(widgets[toolType], "Unknown tool type: " + toolType); widgets[toolType](toolName, options.list, this.element, config); } ListAnalysis.prototype = { constructor: ListAnalysis /** * The events that this component may emit. * @memberof Biojs.InterMine.ListWidgets */ ,eventTypes: [ /** * Called when the user has clicked on a match. * @event * @name Biojs.InterMine.ListAnalysis#onClickMatch * @param {function} callback The function to call. * @eventData {string} identifier The identifier of the object. * @eventData {string} type The Type of the object. * * @example * widget.addListener('onClickMatch', function (ident, type) { * alert("User clicked on the " + type + " " + ident); * }); */ 'onClickMatch', /** * Called when the user has clicked on a button to show results. * @event * @name Biojs.InterMine.ListAnalysis#onClickViewResults * @param {function} callback The function to call. * @eventData {Object} query * An InterMine query object (suitable for passing to intermine.Service#query). * * @example * widget.addListener('onClickViewResults', function (query) { * alert("The user wants to view the results of " + query); * }); */ 'onClickViewResults', /** * Called when the user has clicked on a button to create a list. * @event * @name Biojs.InterMine.ListAnalysis#onClickCreateList * @param {function} callback The function to call. * @eventData {Object} query * An InterMine query object (suitable for passing to intermine.Service#query). * * @example * widget.addListener('onClickCreateList', function (query) { * alert("The user wants to save the results of " + query + " as a list"); * }); */ 'onClickCreateList' ] /** * The ListWidgets instance this component wraps. * @memberof Biojs.InterMine.ListWidgets * @instance * @member {ListWidgets} */ ,widgets: null /** * Listen for onClickMatch events. * @memberof Biojs.InterMine.ListWidgets * @instance * * @param {ListAnalysis~clickMatchHandler} handler The click handler. */ ,onClickMatch: function (handler) { this.addListener('onClickMatch', handler); } /** * Listen for onClickViewResults events. * @memberof Biojs.InterMine.ListWidgets * @instance * * @param {ListAnalysis~queryDefinitionHandler} handler The click handler. */ ,onClickViewResults: function (handler) { this.addListener('onClickViewResults', handler); } /** * Listen for onClickCreateList events. * @memberof Biojs.InterMine.ListWidgets * @instance * * @param {ListAnalysis~queryDefinitionHandler} handler The click handler. */ ,onClickCreateList: function (handler) { this.addListener('onClickCreateList', handler); } /** * Add a event handler for the named event. * @memberof Biojs.InterMine.ListWidgets * @instance * * @param {string} eventName * The name of the event to handle. * @param {function} callback * The handler to call when events of this name are received. * */ ,addListener: function (eventName, callback) { if (!(eventName && callback)) { throw new Error("Both eventName and callback are required arguments"); } if ("string" !== typeof eventName) { throw new Error(eventName + " is not a string"); } if ("function" !== typeof callback) { throw new Error(callback + " is not a function"); } var cbs = this._cbs; var cbsForThisEvent = (cbs[eventName] || (cbs[eventName] = [])); cbsForThisEvent.push(callback); } /** * Receive notification of all events. * * This function proxies all events to the supplied listener. * * The event type is not defined, so this listener should be capable of * handling variadic arguments. * * @memberof Biojs.InterMine.ListWidgets * @instance * @param {function} callback * @eventData {string} eventName The name of the event. * @eventData {Object...} args The event arguments. */ ,onAllEvents: function (callback) { if ("function" !== typeof callback) { throw new Error(callback + " is not a function."); } this._globalListeners.push(callback); } /** * Remove this component from the DOM and release all its resources. * * This method should always be called if the component is to be disposed of, * or else event handler callbacks will likely result in memory leaks. * * @memberof Biojs.InterMine.ListWidgets * @instance * @example * widget.destroy(); * */ ,destroy: function () { this._cbs = {}; this._globalListeners = []; this.widgets = null; this.element && (this.element.innerHTML = ''); this.element = null; } /** * Fire an event with the given name and event data. * * @memberof Biojs.InterMine.ListWidgets * @instance * @private * @param {string} eventName * The name of the event to trigger. * @param {Object...} args * The event arguments to send to handlers. */ ,fireEvent: function (eventName) { if (!eventName) { throw new Error("Not enough arguments - at least one is required."); } var args = [].slice.call(arguments, 1) , cbs = this._cbs , cbsForThisEvent = (cbs[eventName] || (cbs[eventName] = [])).slice() , globalListeners = this._globalListeners.slice() , cb; while (cb = globalListeners.shift()) { cb.apply(null, arguments); } while (cb = cbsForThisEvent.shift()) { cb.apply(null, args); } } }; /** @private **/ function check(test, message) { if (!test) throw new Error(message); } /** @private **/ function resolveTarget(target) { if (!target) throw new Error("No target specified"); if (typeof target == 'string') { if (ELEMENT_ID.test(target)) { return document.getElementById(target); } else { return document.querySelector(target); } } else { if (target.length > 1) throw new Error("Multiple elements in target selection"); return target; } } })(window.Biojs || (window.Biojs = {}));