/** * Main container of the BioJS library. It is the parent class for all the components. * * @namespace * */ var Biojs = function() { // dummy }; /** * @class * @param {string} eventType The name of the event. */ Biojs.EventHandler = function(eventType) { /** * The name of the event. * @type {string} */ this.eventType = eventType; /** * Array of the registered listeners. * @type {function[]} */ this.listeners = []; /** * Register action listeners for the event. * @param {function} actionPerformed The action listener to be registered. */ this.addListener = function ( actionPerformed ) { if ( (typeof actionPerformed) == "function" ) { this.listeners.push(actionPerformed); } }; /** * Executes all listener actions registered in the listeners field. * @param {Object} eventObject The event' object to be passed as argument to the listeners. */ this.triggerEvent = function( eventObject ) { for ( var i in this.listeners ) { this.listeners[i](eventObject); } } }; /** * @class * @param {string} type The name of the event. * @param {Object} data An object containing the data for copying it in this event. * @param {Biojs} source The component source of the event. */ Biojs.Event = function ( type, data, source ) { /** * The component which did triggered the event. * @type {Biojs} */ this.source = source; /** * The name of the event. * @type {string} */ this.type = type; for ( var key in data ) { this[key] = data[key]; } }; /** * @class * */ Biojs.Utils = { /** * Clone all members from an object. * @param {object} object The object to be cloned. * @returns {object} A Clone of the object passed as argument. * */ clone: function(obj) { var newObj = (obj instanceof Array) ? [] : {}; for (i in obj) { if (obj[i] && typeof obj[i] == "object") { newObj[i] = Biojs.Utils.clone(obj[i]); } else { newObj[i] = obj[i]; } } return newObj; }, /** * Determine if an onject or array is empty. * @param {object|array} o Either object or array to figure out if empty or not. * @returns {bool} true if empty, false if don't */ isEmpty: function(o){ if (o instanceof Array) { return (o.length<=0); } else { for (var i in o) { if (o.hasOwnProperty(i)) { return false; } } return true; } }, /** * Cross-browser console for debugging. * The console is disabled by default. That means, all messages written by means Biojs.console.log("My Message") will be ignored. * Use Biojs.console.enable() to enable it. * * @example * // Enabling loggin messages * Biojs.console.enable(); * ... * // Writing a log * Biojs.console.log("My Message"); * * @type {Object} */ console: { enable: function() { // Define a cross-browser window.console.log method. // For IE and FF without Firebug, fallback to using an alert. if (window.console) { /** * @ignore */ // In this case, there are a console, perfect! this.log = function (msg) { console.log(msg) }; } else { // We have not window.console, but it is Opera browser? if (window.opera) { /** * @ignore */ // Right! then lets use window.opera.postError this.log = function (msg) { window.opera.postError(msg) }; } else { // None console found! // Try to write the logs somewhere // That's it! in a new window, identified by 'Biojs.console' var consoleWin = window.open('','myconsole', 'width=350,height=250' +',menubar=0' +',toolbar=0' +',status=0' +',scrollbars=1' +',resizable=1'); // We got it? if (consoleWin) { // Good, build a blank document into with a DIV 'Biojs.console' consoleWin.document.writeln( 'BioJS Console' +'' +'
' +'' ); consoleWin.document.close(); Biojs.console.domDocument = consoleWin.document; Biojs.console.domDivNode = consoleWin.document.getElementById("Biojs.console"); /** * @ignore */ // Finally, the log function will write into the DIV this.log = function (msg) { var message = ''; if (msg instanceof Array) { for ( i=0; i < msg.length; i++ ) { message += '[' + i + ']=' + msg[i] + ' '; } } else if (msg instanceof String || typeof msg === "string") { message = msg; } else { for (var i in msg) { message += '[' + i + ']=' + msg[i] + ' '; } } textNode = Biojs.console.domDocument.createTextNode(message); line = Biojs.console.domDocument.createElement('pre'); line.appendChild(textNode); Biojs.console.domDivNode.appendChild(line); }; } else { // Game over! do not write logs, but let's tell to user by means an alert (sorry!) alert("Please activate the pop-up window in this page " + "in order to enable the BioJS console"); } } } }, log: function (msg) { ; /* Do nothing by default */ } } }; /** * Extend this class Biojs in order to create a new component. * @param {object} instance The subclass. * @param {object} interface (optional) A second parameter passed to the extend method of a class defines the class interface. * @returns {object} SubClass The class with its own members and the inherited ones from Biojs. * * @example * Biojs.MyComponent = Biojs.extend( * { // instance * constructor: function(options) { * // constructor code here * }, * * opt: { target: "divId" }, * * eventTypes: [ "myEvent1", "myEvent2" ], * * getVersion: function() { * return Biojs.MyComponent.VERSION; * } * }, * { // class interface * VERSION: "3.14.15" * }); * * alert(Biojs.MyComponent.VERSION); * */ Biojs.extend = function(_child, _static) { // subclass var extend = Biojs.prototype.extend; // build the prototype Biojs._prototyping = true; /** * @name proto * @constructs */ var proto = new this; // Inherit parent' events to the child if (proto.eventTypes instanceof Array) { for ( var i in proto.eventTypes ) { _child.eventTypes.push(proto.eventTypes[i]); } } // Inherit parent' options to the child if (proto.opt instanceof Object) { for ( var key in proto.opt ) { _child.opt[key] = proto.opt[key]; } } extend.call(proto, _child); /** * @ignore */ proto.base = function() { // call this method from any other method to invoke that method's ancestor }; delete Biojs._prototyping; // create the wrapper for the constructor function var constructor = proto.constructor; var klass = proto.constructor = function() { if (!Biojs._prototyping) { if (this.constructor == klass) { // instantiation // Create a instance of this class function BiojsComponent() {}; BiojsComponent.prototype = proto; var instance = new BiojsComponent(); // Change the default option's values // in the instance by the provided ones instance.setOptions(arguments[0]); // Set the event handlers instance.setEventHandlers(instance.eventTypes); // Set the unique id for the instance instance.biojsObjectId = Biojs.uniqueId(); // register instance Biojs.addInstance(instance); // execute the instance's constructor constructor.apply(instance, arguments); // return the instance return instance; } else { // Calling to ancestor's constructor constructor.apply(this,arguments); } } }; // build the class interface klass.ancestor = this; klass.extend = this.extend; klass.forEach = this.forEach; klass.implement = this.implement; klass.prototype = proto; /** * @ignore */ klass.valueOf = function(type) { return (type == "object") ? klass : constructor.valueOf(); }; klass.toString = this.toString; extend.call(klass, _static); // class initialization if (typeof klass.init == "function") { klass.init(); } return klass; }; Biojs.prototype = /** @lends Biojs# */ { extend: function(source, value) { if (arguments.length > 1) { // extending with a name/value pair var ancestor = this[source]; if (ancestor && (typeof value == "function") && // overriding a method? // the valueOf() comparison is to avoid circular references (!ancestor.valueOf || ancestor.valueOf() != value.valueOf()) && /\bbase\b/.test(value)) { // get the underlying method var method = value.valueOf(); // override value = function() { var previous = this.base || Biojs.prototype.base; this.base = ancestor; var returnValue = method.apply(this, arguments); this.base = previous; return returnValue; }; // point to the underlying method value.valueOf = function(type) { return (type == "object") ? value : method; }; value.toString = Biojs.toString; } this[source] = value; } else if (source) { // extending with an object literal var extend = Biojs.prototype.extend; // if this object has a customised extend method then use it if (!Biojs._prototyping && typeof this != "function") { extend = this.extend || extend; } var proto = {toSource: null}; // do the "toString" and other methods manually var hidden = ["constructor", "toString", "valueOf"]; // if we are prototyping then include the constructor var i = Biojs._prototyping ? 0 : 1; while (key = hidden[i++]) { if (source[key] != proto[key]) { extend.call(this, key, source[key]); } } // copy each of the source object's properties to this object for (var key in source) { if (!proto[key]) extend.call(this, key, source[key]); } } return this; }, /** * Register a function under an event type in order to execute it whenever the event is triggered. * @param {string} eventType The event to be listened. * @param {function} actionPerformed The action to be executed whenever the event occurs. it * * * @example * * var listener = function(eventObj){ * alert("Selected: "+eventObj.start+", end: "+ eventObj.end); * } * * var mySequence = new Biojs.Sequence( { * sequence : "mlpglallllaawtaralevptdgnagllaepqiamfcgrlnmhmnvqngsgtktcidtkegilqy", * target : "#div0001", * format : 'CODATA', * id : 'P918283' * }); * * mySequence.addListener('onSelectionChanged', listener); * * // HTML div tag with the id='div0001' must exist in the HTML document * */ addListener: function(eventType, actionPerformed) { if (this._eventHandlers) { // register the listener in this._eventHandlers for the eventType for(var key in this._eventHandlers) { if ( eventType == this._eventHandlers[key].eventType ) { this._eventHandlers[key].addListener( actionPerformed ); return; } } } }, /** * Sets an event handler and an alias method for each string in the array eventTypes. * This method is executed automatically before constructing an instance, using the eventTypes array * that should be defined as member of subclass. Then, the resulting instance will have methods * named in the form instance.≶eventName>(actionPerformed) for all eventTypes. * * @param {string[]} eventTypes Array of names of the events to be set. * */ setEventHandlers: function (eventTypes) { // Supposed that this._eventHandlers does not exist. this._eventHandlers = []; // Because the event handlers are not initialized yet var alias = function (handler) { return function (actionPerformed) { handler.listeners.push(actionPerformed); } }; if ( typeof eventTypes == "object" ) { // Create an event handler for each eventType in eventTypes for ( var i=0; i < eventTypes.length; i++ ) { var handler = new Biojs.EventHandler( eventTypes[i] ); this._eventHandlers.push( handler ); // Creates the alias this. () // as alternative to be used instead of this.addistener(, ) this[ eventTypes[i] ] = new alias(handler); } } }, /** * Trigger the registered functions under an event type. * @param {string} eventType The event to be raised. * @param {Object} params The values to be included into Biojs.Event object. * * @example * * Biojs.MyComponent = Biojs.extend({ * // ... * // code before the event * * this.raiseEvent('onSelectionChanged', {start : start, end : end}); * * // code after the event * // ... * }); * */ raiseEvent : function(eventType, eventObj) { for(var key in this._eventHandlers ) { if ( eventType == this._eventHandlers[key].eventType ) { this._eventHandlers[key].triggerEvent( eventObj ); return; } } }, // // Save the option values to be applied to this component // options -> [Object] containing the values // setOptions : function (options) { if ( this.opt instanceof Object ) { this.opt = Biojs.Utils.clone(this.opt); for ( var key in options ) { this.opt[key] = options[key]; } } }, // // // source -> [BioJs] the another component // eventType -> [string] the event to be listened // callbackFunction -> [function] the action to be executed // /** * Connect this component with another by means listening its events. * @param {Biojs} source The another component. * @param {string} eventType The event to be listened. * @param {function} actionPerformed The action to be executed whenever the event occurs. it * * * @example * * var mySequence = new Biojs.Sequence( { * sequence : "mlpglallllaawtaralevptdgnagllaepqiamfcgrlnmhmnvqngsgtktcidtkegilqy", * target : "#div0001", * format : 'CODATA', * id : 'P918283' * }); * * var anotherSequence = new Biojs.Sequence({ * sequence : "laawtaralevptmlpglallldgnagllaepqi", * target : "#div0002", * }); * * anotherSequence.listen( * mySequence, * "onSelectionChange", * function( eventObj ) { * anotherSequence.setSelection(eventObj.start, eventObj.end); * } * ); * */ listen: function ( source, eventType, callbackFunction ) { if ( source instanceof Biojs ){ if ( typeof callbackFunction == "function" ) { source.addListener(eventType, callbackFunction); } } }, getId: function () { return this.biojsObjectId; } }; // initialize Biojs = Biojs.extend({ constructor: function() { this.extend(arguments[0]); }, vaueOf: function () { return "Biojs" } }, /** @static */ /** @lends Biojs */ { /** * Ancestor of the Biojs class (Object). * @type {Object} */ ancestor: Object, /** * Version of the Biojs class. * @type {string} */ version: "1.0", forEach: function(object, block, context) { for (var key in object) { if (this.prototype[key] === undefined) { block.call(context, object[key], key, object); } } }, implement: function() { for (var i = 0; i < arguments.length; i++) { if (typeof arguments[i] == "function") { // if it's a function, call it arguments[i](this.prototype); } else { // add the interface using the extend method this.prototype.extend(arguments[i]); } } return this; }, /** * Get string. * @type {function} */ toString: function() { return String(this.valueOf()); }, /** * Get a unique identifier. It is useful to assign the instance' id * @type {function} */ uniqueId: function() { if ( typeof Biojs.prototype.__uniqueid == "undefined" ) { Biojs.prototype.__uniqueid = 0; } return Biojs.prototype.__uniqueid++; }, /** * Register a Biojs instance. * @type {function} */ addInstance: function ( instance ) { if ( typeof Biojs.prototype.__instances == "undefined" ) { Biojs.prototype.__instances = {}; } return Biojs.prototype.__instances[instance.biojsObjectId] = instance; }, /** * Get a Biojs instance by means of its id. * @type {function} */ getInstance: function ( id ) { return Biojs.prototype.__instances[id]; }, /** * Set a variable in the DOM window. * @type {function} */ registerGlobal: function ( key, value ) { window[key] = value; }, /** * Get a variable value from the DOM window. * @type {function} */ getGlobal: function(key){ return window[key]; }, /** * Cross-browser console for debugging. * This is a shorcut for {@link Biojs.Utils.console} * * @type {Object} * */ console: Biojs.Utils.console, EventHandler: Biojs.EventHandler, Event: Biojs.Event, Utils: Biojs.Utils });