diff --git a/datatypes_conf.xml.sample b/datatypes_conf.xml.sample index c89ce67024d..96018c9850b 100644 --- a/datatypes_conf.xml.sample +++ b/datatypes_conf.xml.sample @@ -29,6 +29,7 @@ + diff --git a/lib/galaxy/datatypes/indexers/interval.py b/lib/galaxy/datatypes/indexers/interval.py index ddd5ef9d0e3..8d67aa89a0e 100644 --- a/lib/galaxy/datatypes/indexers/interval.py +++ b/lib/galaxy/datatypes/indexers/interval.py @@ -18,6 +18,7 @@ import tempfile from bisect import bisect def divide( intervals, out_path ): + manifest = {} current_file = None lastchrom = "" for line in intervals: @@ -25,6 +26,7 @@ def divide( intervals, out_path ): chrom = line.chrom except AttributeError, e: continue + manifest[chrom] = max(manifest.get(chrom,0),line.end) if not lastchrom == chrom: if current_file: current_file.flush() @@ -34,6 +36,7 @@ def divide( intervals, out_path ): lastchrom = chrom current_file.flush() current_file.close() + return manifest if __name__ == "__main__": options, args = doc_optparse.parse( __doc__ ) @@ -56,5 +59,9 @@ if __name__ == "__main__": end_col=end_col_1, strand_col=strand_col_1, fix_strand=True ) - divide( interval, out_path ) + manifest = divide( interval, out_path ) + manifest_file = open( os.path.join( out_path, "manifest.tab" ),"w" ) + for key, value in manifest.items(): + print >> manifest_file, "%s\t%s" % (key, value) + manifest_file.close() temp_file.close() diff --git a/lib/galaxy/datatypes/interval.py b/lib/galaxy/datatypes/interval.py index 596f03c36e1..5e4fdcaa4cc 100644 --- a/lib/galaxy/datatypes/interval.py +++ b/lib/galaxy/datatypes/interval.py @@ -298,10 +298,10 @@ class Interval( Tabular ): record_start = int(fields[dataset.metadata.startCol-1]) record_end = int(fields[dataset.metadata.endCol-1]) if record_start < end and record_end > start: - window.append( fields ) #Yes I did want to use a generator here, but it doesn't work downstream + window.append( (record_start, record_end) ) #Yes I did want to use a generator here, but it doesn't work downstream return window - def get_track_resolution( self, dataset, data, start, end): + def get_track_resolution( self, dataset, start, end): return None class Bed( Interval ): diff --git a/lib/galaxy/datatypes/test/temp.txt b/lib/galaxy/datatypes/test/temp.txt new file mode 100644 index 00000000000..17d07eb79ef --- /dev/null +++ b/lib/galaxy/datatypes/test/temp.txt @@ -0,0 +1,2 @@ +1 2 +3 4 diff --git a/lib/galaxy/tracks/store.py b/lib/galaxy/tracks/store.py index 3f06da01089..51e8cfa83d1 100644 --- a/lib/galaxy/tracks/store.py +++ b/lib/galaxy/tracks/store.py @@ -18,9 +18,24 @@ class TrackStoreManager( object ): return TrackStore( path=s.substitute(TemplateSubber(dataset)) ) class TrackStore( object ): + MANIFEST_NAME = "manifest.tab" + def __init__(self, path=""): self.path = path + def get_manifest( self ): + if not self.exists: raise self.DoesNotExist("TrackStore at %s does not exist." % self.path) + manifest_path = os.path.join( self.path, self.MANIFEST_NAME ) + if os.path.exists( manifest_path ): + manifest = {} + for line in open( manifest_path ): + line = line.rstrip("\n\r") + fields = line.split("\t") + manifest[fields[0]] = fields[1:] + return manifest + else: + raise self.DoesNotExist( "Manifest for TrackStore object could not be found." ) + def get(self, chrom="chr1", resolution=None, **kwargs): if not self.exists: raise self.DoesNotExist("TrackStore at %s does not exist." % self.path) object_path = self._get_object_path( chrom, resolution ) diff --git a/lib/galaxy/web/controllers/tracks.py b/lib/galaxy/web/controllers/tracks.py new file mode 100644 index 00000000000..d14c34bcf0f --- /dev/null +++ b/lib/galaxy/web/controllers/tracks.py @@ -0,0 +1,140 @@ +from mako import exceptions +from mako.template import Template +from mako.lookup import TemplateLookup +from galaxy.web.base.controller import * +from galaxy.web.framework import simplejson +from galaxy import web +from galaxy.tracks import messages +import mimeparse +from galaxy.util.json import to_json_string + +class MultiResponse(object): + """ + Shamelessly ripped off of a django snippet. + """ + def __init__(self, handlers): + self.handlers = handlers + + def __call__(self, view_func): + def wrapper(that, trans, *args, **kwargs): + data_resource = view_func(that, trans, *args, **kwargs) + content_type = mimeparse.best_match(self.handlers.keys(), + trans.request.environ['HTTP_ACCEPT']) + response = self.handlers[content_type](data_resource, trans) + trans.response.headers['Content-Type'] = "%s" % content_type + return response + return wrapper + + @classmethod + def JSON( cls, data_resource, trans ): + return simplejson.dumps( data_resource ) + + class XML( object ): + def __call__(self, data_resource, trans ): + raise NotImplementedError( "XML MultiResponse handler is not implemented." ) + + class AMF( object ): + def __call__(self, data_resource, trans ): + raise NotImplementedError( "XML MultiResponse handler is not implemented." ) + + class HTML( object ): + def __init__(self, template ): + self.template = template + + def __call__(self, data_resource, trans ): + return trans.fill_template( self.template, **data_resource ) + + class DEBUG( HTML ): + def __call__(self, data_resource, trans ): + return trans.fill_template( self.template, data_resource=data_resource) + +class WebRoot( BaseController ): + + @web.expose + @MultiResponse( {'text/html':MultiResponse.JSON, + 'text/javascript':MultiResponse.JSON} ) + def dbkeys(self, trans ): + return list(set([x.metadata.dbkey for x in trans.get_history().datasets if not x.deleted])) + + @web.expose + @MultiResponse( {'text/html':MultiResponse.JSON, + 'text/javascript':MultiResponse.JSON} ) + def chroms(self, trans, dbkey ): + db_manifest = os.path.join( trans.app.config.tool_data_path, 'shared','ucsc','chrom', "%s.len" % dbkey ) + manifest = {} + if os.path.exists( db_manifest ): + for line in open( db_manifest ): + line = line.rstrip("\r\n") + fields = line.split("\t") + manifest[fields[0]] = int(fields[1]) + else: + # try to fake a manifest by reading track stores + datasets = trans.app.model.HistoryDatasetAssociation.filter_by(deleted=False, history_id=trans.history.id).all() + for dataset in datasets: + if not dataset.metadata.dbkey == dbkey: continue + track_store = trans.app.track_store.get( dataset ) + if track_store.exists: + try: + for chrom, fields in track_store.get_manifest().items(): + manifest[chrom] = max(manifest.get(chrom, 0), int(fields[0])) + except track_store.DoesNotExist: + pass + return manifest + + @web.expose + @MultiResponse( {'text/html':MultiResponse.JSON, + 'text/javascript':MultiResponse.JSON} ) + def list(self, trans, dbkey ): + datasets = trans.app.model.HistoryDatasetAssociation.filter_by(deleted=False, history_id=trans.history.id).all() + dataset_list = {} + for dataset in datasets: + if dataset.metadata.dbkey == dbkey and trans.app.datatypes_registry.get_indexers_by_datatype( dataset.extension ): + dataset_list[dataset.id] = dataset.name + return dataset_list + + @web.expose + @MultiResponse( {'text/html':MultiResponse.JSON, + 'text/javascript':MultiResponse.JSON} ) + def data(self, trans, dataset_id, chrom="", start="", end=""): + dataset = trans.app.model.HistoryDatasetAssociation.get( dataset_id ) + if not dataset: return messages.NO_DATA + if dataset.state == trans.app.model.Job.states.ERROR: + return messages.NO_DATA + if not dataset.state == trans.app.model.Job.states.OK: + return messages.PENDING + track_store = trans.app.track_store.get( dataset ) + if not track_store.exists: + # Test if we can make a track + indexers = trans.app.datatypes_registry.get_indexers_by_datatype( dataset.extension ) + if indexers: + tool = indexers[0] # They are sorted by class chain so use the top one + # If we can, return pending and launch job + job = trans.app.model.Job() + job.session_id = trans.get_galaxy_session().id + job.history_id = trans.history.id + job.tool_id = tool.id + job.tool_version = "1.0.0" + job.add_input_dataset( "input_dataset", dataset ) + job.add_parameter( "input_dataset", to_json_string( dataset.id ) ) + # This is odd + job.add_output_dataset( "input_dataset", dataset ) + # create store path, this is rather unclear? + track_store.set() + job.add_parameter( "store_path", to_json_string( track_store.path ) ) + job.flush() + trans.app.job_manager.job_queue.put( job.id, tool ) + return messages.PENDING + else: + return messages.NO_DATA + else: + if chrom and start and end: + resolution = dataset.datatype.get_track_resolution( dataset, int(start), int(end ) ) + data = track_store.get( chrom, resolution ) + window = dataset.datatype.get_track_window( dataset, data, int(start), int(end) ) + return window + else: + return messages.DATA + + @web.expose + def index(self, trans, **kwargs): + return trans.fill_template( '/tracks/index.mako' ) diff --git a/lib/mimeparse.py b/lib/mimeparse.py new file mode 100755 index 00000000000..f9b6bb49dde --- /dev/null +++ b/lib/mimeparse.py @@ -0,0 +1,181 @@ +"""MIME-Type Parser + +This module provides basic functions for handling mime-types. It can handle +matching mime-types against a list of media-ranges. See section 14.1 of +the HTTP specification [RFC 2616] for a complete explanation. + + http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1 + +Contents: + - parse_mime_type(): Parses a mime-type into its component parts. + - parse_media_range(): Media-ranges are mime-types with wild-cards and a 'q' quality parameter. + - quality(): Determines the quality ('q') of a mime-type when compared against a list of media-ranges. + - quality_parsed(): Just like quality() except the second parameter must be pre-parsed. + - best_match(): Choose the mime-type with the highest quality ('q') from a list of candidates. +""" + +__version__ = "0.1.2" +__author__ = 'Joe Gregorio' +__email__ = "joe@bitworking.org" +__credits__ = "" + +def parse_mime_type(mime_type): + """Carves up a mime-type and returns a tuple of the + (type, subtype, params) where 'params' is a dictionary + of all the parameters for the media range. + For example, the media range 'application/xhtml;q=0.5' would + get parsed into: + + ('application', 'xhtml', {'q', '0.5'}) + """ + parts = mime_type.split(";") + params = dict([tuple([s.strip() for s in param.split("=")])\ + for param in parts[1:] ]) + full_type = parts[0].strip() + # Java URLConnection class sends an Accept header that includes a single "*" + # Turn it into a legal wildcard. + if full_type == '*': full_type = '*/*' + (type, subtype) = full_type.split("/") + return (type.strip(), subtype.strip(), params) + +def parse_media_range(range): + """Carves up a media range and returns a tuple of the + (type, subtype, params) where 'params' is a dictionary + of all the parameters for the media range. + For example, the media range 'application/*;q=0.5' would + get parsed into: + + ('application', '*', {'q', '0.5'}) + + In addition this function also guarantees that there + is a value for 'q' in the params dictionary, filling it + in with a proper default if necessary. + """ + (type, subtype, params) = parse_mime_type(range) + if not params.has_key('q') or not params['q'] or \ + not float(params['q']) or float(params['q']) > 1\ + or float(params['q']) < 0: + params['q'] = '1' + return (type, subtype, params) + +def fitness_and_quality_parsed(mime_type, parsed_ranges): + """Find the best match for a given mime-type against + a list of media_ranges that have already been + parsed by parse_media_range(). Returns a tuple of + the fitness value and the value of the 'q' quality + parameter of the best match, or (-1, 0) if no match + was found. Just as for quality_parsed(), 'parsed_ranges' + must be a list of parsed media ranges. """ + best_fitness = -1 + best_fit_q = 0 + (target_type, target_subtype, target_params) =\ + parse_media_range(mime_type) + for (type, subtype, params) in parsed_ranges: + if (type == target_type or type == '*' or target_type == '*') and \ + (subtype == target_subtype or subtype == '*' or target_subtype == '*'): + param_matches = reduce(lambda x, y: x+y, [1 for (key, value) in \ + target_params.iteritems() if key != 'q' and \ + params.has_key(key) and value == params[key]], 0) + fitness = (type == target_type) and 100 or 0 + fitness += (subtype == target_subtype) and 10 or 0 + fitness += param_matches + if fitness > best_fitness: + best_fitness = fitness + best_fit_q = params['q'] + + return best_fitness, float(best_fit_q) + +def quality_parsed(mime_type, parsed_ranges): + """Find the best match for a given mime-type against + a list of media_ranges that have already been + parsed by parse_media_range(). Returns the + 'q' quality parameter of the best match, 0 if no + match was found. This function bahaves the same as quality() + except that 'parsed_ranges' must be a list of + parsed media ranges. """ + return fitness_and_quality_parsed(mime_type, parsed_ranges)[1] + +def quality(mime_type, ranges): + """Returns the quality 'q' of a mime-type when compared + against the media-ranges in ranges. For example: + + >>> quality('text/html','text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5') + 0.7 + + """ + parsed_ranges = [parse_media_range(r) for r in ranges.split(",")] + return quality_parsed(mime_type, parsed_ranges) + +def best_match(supported, header): + """Takes a list of supported mime-types and finds the best + match for all the media-ranges listed in header. The value of + header must be a string that conforms to the format of the + HTTP Accept: header. The value of 'supported' is a list of + mime-types. + + >>> best_match(['application/xbel+xml', 'text/xml'], 'text/*;q=0.5,*/*; q=0.1') + 'text/xml' + """ + parsed_header = [parse_media_range(r) for r in header.split(",")] + weighted_matches = [(fitness_and_quality_parsed(mime_type, parsed_header), mime_type)\ + for mime_type in supported] + weighted_matches.sort() + return weighted_matches[-1][0][1] and weighted_matches[-1][1] or '' + +if __name__ == "__main__": + import unittest + + class TestMimeParsing(unittest.TestCase): + + def test_parse_media_range(self): + self.assert_(('application', 'xml', {'q': '1'}) == parse_media_range('application/xml;q=1')) + self.assertEqual(('application', 'xml', {'q': '1'}), parse_media_range('application/xml')) + self.assertEqual(('application', 'xml', {'q': '1'}), parse_media_range('application/xml;q=')) + self.assertEqual(('application', 'xml', {'q': '1'}), parse_media_range('application/xml ; q=')) + self.assertEqual(('application', 'xml', {'q': '1', 'b': 'other'}), parse_media_range('application/xml ; q=1;b=other')) + self.assertEqual(('application', 'xml', {'q': '1', 'b': 'other'}), parse_media_range('application/xml ; q=2;b=other')) + # Java URLConnection class sends an Accept header that includes a single * + self.assertEqual(('*', '*', {'q': '.2'}), parse_media_range(" *; q=.2")) + + def test_rfc_2616_example(self): + accept = "text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5" + self.assertEqual(1, quality("text/html;level=1", accept)) + self.assertEqual(0.7, quality("text/html", accept)) + self.assertEqual(0.3, quality("text/plain", accept)) + self.assertEqual(0.5, quality("image/jpeg", accept)) + self.assertEqual(0.4, quality("text/html;level=2", accept)) + self.assertEqual(0.7, quality("text/html;level=3", accept)) + + def test_best_match(self): + mime_types_supported = ['application/xbel+xml', 'application/xml'] + # direct match + self.assertEqual(best_match(mime_types_supported, 'application/xbel+xml'), 'application/xbel+xml') + # direct match with a q parameter + self.assertEqual(best_match(mime_types_supported, 'application/xbel+xml; q=1'), 'application/xbel+xml') + # direct match of our second choice with a q parameter + self.assertEqual(best_match(mime_types_supported, 'application/xml; q=1'), 'application/xml') + # match using a subtype wildcard + self.assertEqual(best_match(mime_types_supported, 'application/*; q=1'), 'application/xml') + # match using a type wildcard + self.assertEqual(best_match(mime_types_supported, '*/*'), 'application/xml') + + mime_types_supported = ['application/xbel+xml', 'text/xml'] + # match using a type versus a lower weighted subtype + self.assertEqual(best_match(mime_types_supported, 'text/*;q=0.5,*/*; q=0.1'), 'text/xml') + # fail to match anything + self.assertEqual(best_match(mime_types_supported, 'text/html,application/atom+xml; q=0.9'), '') + + # common AJAX scenario + mime_types_supported = ['application/json', 'text/html'] + self.assertEqual(best_match(mime_types_supported, 'application/json, text/javascript, */*'), 'application/json') + # verify fitness ordering + self.assertEqual(best_match(mime_types_supported, 'application/json, text/html;q=0.9'), 'application/json') + + def test_support_wildcards(self): + mime_types_supported = ['image/*', 'application/xml'] + # match using a type wildcard + self.assertEqual(best_match(mime_types_supported, 'image/png'), 'image/*') + # match using a wildcard for both requested and supported + self.assertEqual(best_match(mime_types_supported, 'image/*'), 'image/*') + + unittest.main() diff --git a/run.sh b/run.sh old mode 100644 new mode 100755 index fe49fbef491..c9a35d22dba --- a/run.sh +++ b/run.sh @@ -1,4 +1,4 @@ #!/bin/sh cd `dirname $0` -python -ES ./scripts/paster.py serve universe_wsgi.ini $@ +python2.52.5 -ES ./scripts/paster.py serve universe_wsgi.ini $@ diff --git a/static/images/tracks/close_btn.gif b/static/images/tracks/close_btn.gif new file mode 100644 index 00000000000..cac3adf1aca Binary files /dev/null and b/static/images/tracks/close_btn.gif differ diff --git a/static/images/tracks/go_btn.gif b/static/images/tracks/go_btn.gif new file mode 100644 index 00000000000..c5f63383e79 Binary files /dev/null and b/static/images/tracks/go_btn.gif differ diff --git a/static/images/tracks/pan_left.gif b/static/images/tracks/pan_left.gif new file mode 100644 index 00000000000..40db4e86443 Binary files /dev/null and b/static/images/tracks/pan_left.gif differ diff --git a/static/images/tracks/pan_right.gif b/static/images/tracks/pan_right.gif new file mode 100644 index 00000000000..aec905e3552 Binary files /dev/null and b/static/images/tracks/pan_right.gif differ diff --git a/static/images/tracks/show_history.gif b/static/images/tracks/show_history.gif new file mode 100644 index 00000000000..b43d3b29c63 Binary files /dev/null and b/static/images/tracks/show_history.gif differ diff --git a/static/images/tracks/zoom_in.gif b/static/images/tracks/zoom_in.gif new file mode 100644 index 00000000000..bf79c8250bc Binary files /dev/null and b/static/images/tracks/zoom_in.gif differ diff --git a/static/images/tracks/zoom_in_full.gif b/static/images/tracks/zoom_in_full.gif new file mode 100644 index 00000000000..771c97b21e2 Binary files /dev/null and b/static/images/tracks/zoom_in_full.gif differ diff --git a/static/images/tracks/zoom_out.gif b/static/images/tracks/zoom_out.gif new file mode 100644 index 00000000000..bebcd0b6fe1 Binary files /dev/null and b/static/images/tracks/zoom_out.gif differ diff --git a/static/images/tracks/zoom_out_full.gif b/static/images/tracks/zoom_out_full.gif new file mode 100644 index 00000000000..325cc406f93 Binary files /dev/null and b/static/images/tracks/zoom_out_full.gif differ diff --git a/static/scripts/jamal.js b/static/scripts/jamal.js new file mode 100644 index 00000000000..5c19d3c15db --- /dev/null +++ b/static/scripts/jamal.js @@ -0,0 +1,1732 @@ +// prevent execution of jamal if included more than once +if(typeof window.jamal == "undefined") { + +/* SVN FILE: $Id: jamal.js 65 2009-01-19 21:08:03Z teemow $ */ +/** + * This is the Jamal core. Heavily inspired by jQuery's architecture. + * + * To quote Dave Cardwell: + * Built on the shoulders of giants: + * * John Resig - http://jquery.com/ + * + * jQuery is required + * + * Jamal : Javascript MVC Assembly Layout + * Copyright (c) 2006, Timo Derstappen + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright (c) 2006, Timo Derstappen + * @link + * @package jamal + * @subpackage jamal.core + * @since Jamal v 0.1 + * @version $Revision: 65 $ + * @modifiedby $LastChangedBy: teemow $ + * @lastmodified $Date: 2009-01-19 22:08:03 +0100 (Mo, 19. Jan 2009) $ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ + +/** + * Create a new jamal Object + * + * @constructor + * @private + * @name jamal + * @cat core + */ +var jamal = function() { + // If the context is global, return a new object + if (window == this) { + return new jamal(); + } + + return this.configure(); +}; + +/** + * Create the jamal core prototype + * + * @public + * @name jamal + * @cat core + */ +jamal.fn = jamal.prototype = { + /* Properties */ + + /** + * The current version of jamal. + * + * @private + * @property + * @name version + * @type String + * @cat core + */ + version: '0.4.1', + + /** + * Defines the root element with the jamal configuration class. This is + * necessary due to performance. jQuery is a lot faster in finding classes + * when it knows the holding element. + * + * @private + * @property + * @name root + * @type String + * @cat core + */ + root: 'body', + + /** + * Name of the current controller. + * + * @public + * @property + * @name name + * @type String + * @cat core + */ + name: '', + + /** + * Name of the current action. + * + * @public + * @property + * @name action + * @type String + * @cat core + */ + action: '', + + /** + * Current controller object. + * + * @public + * @property + * @name controller + * @type Object + * @cat core + */ + current: {}, + + /** + * Map of all available models. + * + * @public + * @property + * @name m + * @type Map + * @cat core + */ + m: {}, + + /** + * Map of all available views. + * + * @public + * @property + * @name v + * @type Map + * @cat core + */ + v: {}, + + /** + * Map of all available controllers. + * + * @public + * @property + * @name c + * @type Map + * @cat core + */ + c: {}, + + /** + * Jamal configuration passed from the root elements class + * + * @public + * @property + * @name config + * @type Object + * @cat core + */ + config: {}, + + /** + * Debug flag to give more information about jamal in the console. + * + * @private + * @property + * @name debug + * @type Boolean + * @cat core + */ + debug: false, + + /** + * Jamal events + * + * @public + * @property + * @name events + * @type Object + * @cat core + */ + events: {}, + + /* Methods */ + + /** + * Method description + * + * @example jamal.start(); + * @result jamal.current == [ new Controller ] + * + * @public + * @name start + * @type jamal + * @cat core + */ + start: function() { + this.log('Starting the Jamal application (Version: '+this.version+')...'); + this.log('Browser:'); + this.dir(jQuery.browser); + this.log('Controller: ' + this.name); + this.log('Action: ' + this.action); + if (this.debug === true) { + window.console.time('Timing'); + } + var started = this.load(); + if (this.debug === true) { + window.console.timeEnd('Timing'); + } + if (jQuery.browser.mozilla) { + this.log('Jamal size: '+this.toSource().length+' Chars'); + } + + // capture errors + jQuery(window).error(function(message, file, line) { + var e = {'name':'window.onerror', + 'message':message, + 'file':file, + 'line':line, + 'stack':'' + }; + if(jamal.fn === undefined) { + $j.error('Window error captured!', e); + } else { + jamal.fn.error('Window error captured!', e); + } + return true; + }); + + return started; + }, + + /** + * Log messages on the browser console. Firebug is recommended. + * + * @example jamal.log('current controller: ' + this.controller); + * + * @public + * @name start + * @type debug + * @param String message The message to be displayed on the console + * @param String message (optional) More messages to be displayed on the console + * @cat log + */ + log: function(message) { + if (this.debug === true) { + var log = ''; + for (var i=0; i1) { + e = arguments[1]; + window.console.error('Jamal Error: '+message, e); + if(typeof e === "object") { + if(typeof e.message === "object") { + this.log(e.name+': '); + this.dir(e.message); + } else { + this.log(e.name+': '+e.message); + } + this.dir(e); + this.log('Stack: ' + e.stack); + } else { + this.log(e); + this.log('Stack:'); + this.dir(this.callstack()); + } + } else { + window.console.error('Jamal Error: '+message); + } + } + }, + + /** + * This function returns an array of objects that contain the + * information about call stack. + * + * @example callstack = jamal.callstack(); + * + * @public + * @name callstack + * @type debug + * @cat log + */ + callstack: function() { + var re_without_parenthesis = /[(][^)]*[)]/; + var re_file_line = /(.*):(\d+)$/; + + var stack = new Error().stack.split('\n'); + stack.splice(0,2); // remove first two stack frames + + var frames = []; + for(var i in stack) { + // a stack frame string split into parts + var frame = stack[i].split('@'); + if(frame && frame.length == 2) { + frame = { + // Stackframe object + 'name': frame[0], + 'source': frame[0].replace(re_without_parenthesis, ''), + 'file': frame[1].match(re_file_line)[1], // first group + 'line': frame[1].match(re_file_line)[2] // second group + }; + this.log('at ' + frame.file + ' (' + frame.name + ': ' + frame.line + ')'); + } + } + }, + + /** + * Log objects to the console + * + * @example jamal.dir(obj); + * @result [ { prop1: val1, prop2: val2 } ] + * + * @public + * @name dir + * @type debug + * @param Object obj The object which should be logged on the console. + * @cat log + */ + dir: function(obj) { + if (this.debug === true) { + window.console.dir(obj); + } + }, + + /** + * Try to configure jamal + * + * Currently it is expected that there is a dom element with metadata + * attached. This data is read via jQuery's metadata plugin. + * + * This makes it very easy to use jamal with e.g. CakePHP. Just add + * + * to your default layout. Now you only need to create and include the + * corresponding js files. + * + * @example jamal.configure(); + * @before + * @result [ jamal.controller = 'Tests', jamal.action = 'index' ] + * + * @private + * @name configure + * @type jamal + * @cat core + */ + configure: function() { + try { + var data = jQuery(this.root+'.jamal').metadata(); + } catch(e) { + this.debug = true; + this.error('jQuery Metadata Plugin failed to read the configuration. '+ + 'Probably there is no class="jamal {controller:\'example\',action:\'index\'}" in your markup!', e); + } + + if (typeof(data) !== 'object') { + this.debug = true; + this.error('No configuration found!'); + return false; + } else { + this.config = data; + this.name = data.controller; + this.action = data.action; + this.debug = data.debug; + return true; + } + }, + + /** + * Try to load the controller action + * + * @example jamal.load(); + * + * @public + * @name load + * @type mvc + * @cat core + */ + load: function () { + var loaded = false; + if (typeof this.c[this.name] !== 'object') { + jamal.fn = jamal; + $j.c({Generic: {}}); + this.name = 'Generic'; + } + + // controller + try { + this.current = this.c[this.name]; + } catch(e) { + this.error('Controller error!', e); + } + + // callback before the action + this.current.beforeAction(); + + // components + if(this.current.components) { + for(var i in this.current.components) { + try { + this[this.current.components[i]](); + } catch(e) { + this.error(this.current.components[i]+' component error!', e); + } + } + } + + // action + if (typeof this.c[this.name][this.action] === 'function') { + try { + this.current[this.action](); + loaded = true; + } catch(e) { + this.error('Action couldn\'t be started!', e); + } + } else { + this.log('Action not found!'); + } + + // callback after the action + this.current.afterAction(); + return loaded; + }, + + /** + * Run this function to give control of the $j variable back + * to whichever library first implemented it. This helps to make + * sure that jamal doesn't conflict with the $j object + * of other libraries. + * + * By using this function, you will only be able to access jamal + * using the 'jamal' variable. For example, where you used to do + * $j.json("/example/action"), you now must do jamal.json("/example/action"). + * + * @example jamal.noConflict(); + * // Do something with jamal + * jamal.json("/example/action"); + * @desc Maps the original object that was referenced by $j back to $j + * + * @name noConflict + * @type undefined + * @cat core + */ + noConflict: function() { + if (jamal._$) { + $j = jamal._$j; + } + return jamal; + } +}; + +/** + * Extend one object with one or more others, returning the original, + * modified, object. This is a great utility for simple inheritance. + * + * @example var settings = { validate: false, limit: 5, name: "foo" }; + * var options = { validate: true, name: "bar" }; + * jamal.extend(settings, options); + * @result settings == { validate: true, limit: 5, name: "bar" } + * @desc Merge settings and options, modifying settings + * + * @example var defaults = { validate: false, limit: 5, name: "foo" }; + * var options = { validate: true, name: "bar" }; + * var settings = jamal.extend({}, defaults, options); + * @result settings == { validate: true, limit: 5, name: "bar" } + * @desc Merge defaults and options, without modifying the defaults + * + * @name $.extend + * @param Object target The object to extend + * @param Object prop1 The object that will be merged into the first. + * @param Object propN (optional) More objects to merge into the first + * @type Object + * @cat JavaScript + */ +jamal.extend = jamal.fn.extend = function() { + // copy reference to target object + var target = arguments[0], a = 1; + + // extend jamal itself if only one argument is passed + if (arguments.length == 1) { + target = this; + a = 0; + } + var prop; + while ((prop = arguments[a++]) != null) { + // Extend the base object + for (var i in prop) { + target[i] = prop[i]; + } + } + + // Return the modified object + return target; +}; + +/* SVN FILE: $Id: jamal.js 22 2007-06-28 00:09:37Z teemow $ */ +/** + * To quote Dave Cardwell: + * Built on the shoulders of giants: + * * John Resig - http://jquery.com/ + * + * Jamal : Javascript MVC Assembly Layout + * Copyright (c) 2006, Timo Derstappen + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright (c) 2006, Timo Derstappen + * @link + * @package jamal + * @subpackage jamal.core + * @since Jamal v 0.1 + * @version $Revision: 40 $ + * @modifiedby $LastChangedBy: teemow $ + * @lastmodified $Date: 2007-08-20 18:24:35 +0200 (Mo, 20. Aug 2007) $ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ + +/** + * Jamal controller constructor + * + * @name jamal.controller + * @type Object + * @param String controller Name of the constructed controller + * @cat controller + */ +/** + * Inherit a controller from the jamal app controller + * + * @example $j.c({Foos:{ + * index: function(){ + * alert('hello world'); + * } + * }); + * @desc Merge controller into jamal.c map and inherit everything from jamal.controller + * + * @name $j.m + * @param Object controller The controller that will be merged into the controller map. + * @type Object + * @cat core + * @todo merge jamal.c and jamal.controller with function overloading + */ +jamal.c = jamal.fn.c = function(controller) { + if(typeof controller === 'object') { + var inherited; + for(var i in controller) { + inherited = new jamal.fn.c(i); + jamal.extend(inherited, controller[i]); + + // add model + var m = i.substr(0, i.length-1).replace(/ie$/, 'y'); // name is singular + if(jamal.fn.m[m]) { + inherited.m = jamal.fn.m[m]; + } else { + // if no model create one + inherited.m = jamal.fn.m[m] = new jamal.fn.m(m); + } + + // add view + if(jamal.fn.v[i]) { + inherited.v = jamal.fn.v[i]; + } else { + // if no view create one + inherited.v = jamal.fn.v[i] = new jamal.fn.v(i); + } + + controller[i] = inherited; + } + jamal.extend(jamal.fn.c, controller); + } else { + this.name = controller; + } +}; + +jamal.fn.extend(jamal.fn.c.prototype, { + /** + * Callback which get called before an action + * + * Overwrite this method in your own (app)controller + * + * @public + * @name beforeAction + * @cat controller + */ + beforeAction: function(){ + }, + + /** + * Callback which get called after an action + * + * Overwrite this method in your own (app)controller + * + * @public + * @name beforeAction + * @cat controller + */ + afterAction: function(){ + }, + + /** + * (Re-)Initialize a controller + * + * @example jamal.controller.init() + * @desc initializes the current controller action + * + * @example filter = $('#list'); + * jamal.controller.init(filter) + * @desc initializes the current controller action but events are only + * bind to elements in #list + * + * @public + * @name init + * @param Object filter Dom element which should be reinitialized + * @cat controller + */ + init: function(filter){ + jamal.current[jamal.action](filter); + } +}); + +/* SVN FILE: $Id: jamal.js 22 2007-06-28 00:09:37Z teemow $ */ +/** + * To quote Dave Cardwell: + * Built on the shoulders of giants: + * * John Resig - http://jquery.com/ + * + * Jamal : Javascript MVC Assembly Layout + * Copyright (c) 2006, Timo Derstappen + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright (c) 2006, Timo Derstappen + * @link + * @package jamal + * @subpackage jamal.core + * @since Jamal v 0.1 + * @version $Revision: 58 $ + * @modifiedby $LastChangedBy: teemow $ + * @lastmodified $Date: 2009-01-19 15:40:09 +0100 (Mo, 19. Jan 2009) $ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ + +/** + * Jamal model constructor + * + * @name jamal.model + * @type Object + * @param String model Name of the constructed model + * @cat core + */ +/** + * Inherit a model from the jamal app model. + * + * @example $j.m({Foo:{ + * getBar: function(){ + * $j.json('/test/', function(response){ + * }); + * } + * }); + * @desc Merge model into jamal.m map and inherit everything from jamal.model + * + * @name $j.m + * @param Object model The model that will be merged into the model map. + * @type Object + * @cat core + * @todo merge jamal.m and jamal.model with function overloading + */ +jamal.m = jamal.fn.m = function(model) { + if(typeof model === 'object') { + var inherited; + for (var i in model) { + // get the jamal model + inherited = new jamal.fn.m(i); + + // inherit the new model + jamal.extend(inherited, model[i]); + model[i] = inherited; + } + jamal.extend(jamal.fn.m, model); + } else { + this.name = model; + } +}; + +jamal.fn.extend(jamal.fn.m.prototype, { + /** + * A wrapper for jQuerys ajax + * + * We need a wrapper here to add the global callback. Please use jamal.json + * in your controllers/models. + * + * @example jamal.model.json('/test/', + * function(response) { + * jamal.dir(response.data); + * }); + * + * @public + * @name json + * @param String url The URL of the page to load. + * @param Function callback A function to be executed whenever the data is loaded. + * @cat model + * @todo this method should be moved to a general jamal model class + */ + json: function(url, callback) { + var model = this; + + var settings = { + type: 'GET', + dataType: 'json', + url: url, + beforeSend: function(xhr){ + jamal.ajaxSend(xhr); + return model.beforeSend(); + }, + error: function(xhr, type, exception){ + if(model.error()) { + jamal.error('Ajax error: ' + type, exception); + } + }, + complete: function(xhr, result){ + model.complete(xhr, result); + }, + success: function(response) { + jamal.ajaxSuccess(response); + if(model.callback(response)) { + if(callback) { + callback(response); + } + } + } + }; + + jQuery.ajax(settings); + }, + + /** + * A wrapper for jQuerys getJSON + * + * We need a wrapper here to add the global callback. Please use jamal.json + * in your controllers/models. + * + * @example jamal.model.json('/test/', + * function(response) { + * jamal.dir(response.data); + * }); + * + * @public + * @name json + * @param String url The URL of the page to load. + * @param Function callback A function to be executed whenever the data is loaded. + * @cat model + * @todo this method should be moved to a general jamal model class + */ + post: function(url, data, callback) { + var model = this; + + var settings = { + type: 'POST', + dataType: 'json', + url: url, + data: data, + beforeSend: function(xhr){ + jamal.ajaxSend(xhr); + return model.beforeSend(); + }, + error: function(xhr, type, exception){ + if(model.error()) { + jamal.error('Ajax error: ' + type, exception); + } + }, + complete: function(xhr, result){ + model.complete(xhr, result); + }, + success: function(response) { + if(response.error_code) { + $j.error('Response error: ' + response.error_code); + } + jamal.ajaxSuccess(response); + if(model.callback(response)) { + if(callback) { + callback(response); + } + } + } + }; + + jQuery.ajax(settings); + }, + + /** + * A pre-callback to modify the XMLHttpRequest object before it is sent. + * Use this to set custom headers etc. The XMLHttpRequest is passed as the + * only argument. + * + * Overwrite this method in your own (app)model + * + * @public + * @name beforeSend + * @cat model + */ + beforeSend: function(xhr){ + return true; + }, + + /** + * A function to be called when the request finishes (after success and + * error callbacks are executed). The function gets passed two arguments: + * The XMLHttpRequest object and a string describing the type of success + * of the request. + * + * Overwrite this method in your own (app)model + * + * @public + * @name complete + * @cat model + */ + complete: function(xhr, result){ + return true; + }, + + /** + * A function to be called if the request fails. The function gets passed + * three arguments: The XMLHttpRequest object, a string describing the type + * of error that occurred and an optional exception object, if one occurred. + * + * @public + * @name error + * @cat model + */ + error: function(xhr, type, exception){ + return true; + }, + + /** + * A general callback for the model + * + * Jamal expects a JSON response like + * { + * data: {} + * } + * + * @example jamal.model.callback(response, + * function(response){ + * jamal.dir(response.data) + * }); + * + * @public + * @name callback + * @param Object response JSON response from the server. + * @cat model + */ + callback: function(response){ + if(response.error) { + var error = response.error; + $j.error(error.error + ' (' + error.code + '): ' + error.description + ' in ' + error.file); + $j.log('Stack:'); + $j.log(error.stack); + $j.log('Context:'); + $j.log(error.context); + $j.log('Listing:'); + $j.dir(error.listing); + return false; + } + return response; + } +}); + +/** + * Bind the jamal ajax callbacks to the jQuery event handling + * + * @example jamal.ajaxSend(function() { alert("Hello"); }); + * + * @name event + * @type jamal + * @param Function fn A function to bind to the jamal event + * @cat model + */ +(function() { + // Handle ajax event binding + jamal.fn.ajaxSend = function(f){ + return typeof f === 'function' ? jQuery().bind('j_ajaxSend', f) : jQuery.event.trigger('j_ajaxSend', [f]); + }; + jamal.fn.ajaxSuccess = function(f){ + return typeof f === 'function' ? jQuery().bind('j_ajaxSuccess', f) : jQuery.event.trigger('j_ajaxSuccess', [f]); + }; +})(); + +/* SVN FILE: $Id: jamal.js 22 2007-06-28 00:09:37Z teemow $ */ +/** + * To quote Dave Cardwell: + * Built on the shoulders of giants: + * * John Resig - http://jquery.com/ + * + * Jamal : Javascript MVC Assembly Layout + * Copyright (c) 2006, Timo Derstappen + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright (c) 2006, Timo Derstappen + * @link + * @package jamal + * @subpackage jamal.core + * @since Jamal v 0.1 + * @version $Revision: 40 $ + * @modifiedby $LastChangedBy: teemow $ + * @lastmodified $Date: 2007-08-20 18:24:35 +0200 (Mo, 20. Aug 2007) $ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ + +/** + * Jamal view constructor + * + * @name jamal.view + * @type Object + * @param String view Name of the constructed view + * @cat view + */ +/** + * Inherit a view from the jamal app view. + * + * @example $j.v({Foos:{ + * removeMessage: function(){ + * $('div.message').remove(); + * } + * }); + * @desc Merge view into jamal.v map and inherit everything from jamal.view + * + * @name $j.v + * @param Object view The view that will be merged into the view map. + * @type Object + * @cat core + * @todo merge jamal.v and jamal.view with function overloading + */ +jamal.v = jamal.fn.v = function(view) { + if(typeof view === 'object') { + var inherited; + for (var i in view) { + inherited = new jamal.fn.v(i); + jamal.extend(inherited, view[i]); + view[i] = inherited; + } + jamal.extend(jamal.fn.v, view); + } else { + this.name = view; + } +}; + +jamal.fn.extend(jamal.fn.v.prototype, { + /** + * Add a spinner to an element + * + * @name addSpinner + * @param Mixed obj Element / jQuery object / css selector of an dom element which should contain the spinner + * @cat view + */ + addSpinner: function(obj) { + // create spinner + var spinner; + try { + spinner = document.createElement('div'); + spinner.className = 'spinner'; + } catch( ex ) { + jamal.error( 'Cannot create <' + tag + '> element:\n' + + args.toSource() + '\n' + args ); + spinner = null; + } + $(obj).prepend(spinner); + }, + + /** + * Remove all spinner + * + * @name removeSpinner + * @cat view + */ + removeSpinner: function() { + $('div.spinner').remove(); + }, + + /** + * Add a (success) message at the top of the current page + * + * @name addMessage + * @param String message The message that should be displayed + * @cat view + */ + addMessage: function(message){ + $('#content').prepend(message); + }, + + /** + * Remove all messages and errors + * + * @name removeMessages + * @cat view + */ + removeMessages: function() { + $('div.error').remove(); + $('div.message').remove(); + }, + + /** + * Add an error message + * + * @name addError + * @param String message The error message that should be displayed + * @param Mixed obj Element / jQuery object / css selector of an dom element which should contain the error message + */ + addError: function(message, obj) { + $('div.error', obj).remove(); + $(obj).prepend(message); + $('div.error', obj).show(); + }, + + /** + * Decode HTML entities + * + * @example jamal.view.decode_html() + * + * @public + * @name decode_html + * @type jamal + * @cat view + */ + decode_html: function(str) { + if (typeof str === 'string') { + var div = document.createElement('div'); + div.innerHTML = str.replace(/<\/?[^>]+>/gi, ''); + return div.childNodes[0] ? div.childNodes[0].nodeValue : ''; + } else { + return ''; + } + } +}); + +/* SVN FILE: $Id: jamal.js 18 2007-06-13 09:07:32Z teemow $ */ +/** + * To quote Dave Cardwell: + * Built on the shoulders of giants: + * * John Resig - http://jquery.com/ + * + * Jamal : Javascript MVC Assembly Layout + * Copyright (c) 2007, Timo Derstappen + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright (c) 2007, Timo Derstappen + * @link + * @package jamal + * @subpackage jamal.modal + * @since Jamal v 0.4 + * @version $Revision: 40 $ + * @modifiedby $LastChangedBy: teemow $ + * @lastmodified $Date: 2007-08-20 18:24:35 +0200 (Mo, 20. Aug 2007) $ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ + +/** + * Jamal modal component + * + * This component offers a modal dialog for jamal applications. eg. if your + * session timed out you can display a modal login form + * + * @public + * @name jamal + * @cat modal + */ +jamal.fn.extend({ + /** + * Create a modal dialog + * + * @example jamal.modal('

message

'); + * + * @private + * @name modal + * @type jamal + * @param String content Content that will be displayed in the modal window + * @cat modal + */ + modal: function(content) { + if (content) { + if (!jamal.modal.active) { + // deactivate screen + jQuery('body').css('overflow', 'hidden'); + if (jQuery.browser.msie) { + jQuery('select').hide(); + } + jQuery('#wrapper') + .prepend('
') + .prepend('
'+content+'
'); + + jQuery('#jamal_overlay') + .css({'background-color': '#000', + 'position': 'absolute', + 'width': '4000px', + 'height': '4000px', + 'float': 'left', + 'margin-left': '-1500px', + 'top': '0', + 'left': '0', + 'z-index': '80', + 'filter': 'alpha(opacity=50)', + '-moz-opacity': '.50', + 'opacity': '.50'}); + jQuery('#jamal_size').css('position', 'relative'); + jQuery('#jamal_modal') + .css({'position': 'absolute', + 'background-color': '#fff', + 'border': '4px solid #ccc', + 'width': '380px', + 'height': '305px', + 'padding': '10px', + 'z-index': '900'}); + jamal.modal.active = true; + } else { + jQuery('div.jamal_size').html(content); + } + jamal.modal.resize(); + return true; + } else { + return false; + } + } +}); + +jamal.fn.extend(jamal.fn.modal, { + /** + * Flag for modal dialog + * + * @public + * @property + * @name jamal.modal.active + * @type Boolean + * @cat modal + */ + active: false, + + /** + * Resize the current modal dialog + * + * @example jamal.modal.resize(); + * + * @private + * @name resize + * @type jamal + * @cat modal + */ + resize: function() { + // width + jQuery('#jamal_modal').css('width', jQuery('div.jamal_size').width()+'px'); + + var body = jQuery('#wrapper').width(); + var modal = jQuery('#jamal_modal').width(); + jQuery('#jamal_modal').css('margin-left', (body/2-modal/2)+'px'); + + // height + jQuery('#jamal_modal').css('height', jQuery('div.jamal_size').height()+'px'); + if (jQuery.browser.msie) { + var offset = document.documentElement.scrollTop; + body = document.documentElement.clientHeight; + } else { + var offset = window.pageYOffset; + body = window.innerHeight; + } + modal = jQuery('#jamal_modal').height(); + jQuery('#jamal_modal').css('margin-top', (offset + body/2 - modal/2) +'px'); + }, + + /** + * Close the current dialog + * + * @example jamal.close(); + * + * @private + * @name close + * @type jamal + * @cat modal + */ + close: function() { + if (jamal.modal.active) { + jQuery('#jamal_modal').fadeOut('slow'); + jQuery('#jamal_overlay').remove(); + jQuery('body').css('overflow', 'auto'); + if (jQuery.browser.msie) { + jQuery('select').show(); + } + jamal.modal.active = false; + } + } +}); +/* SVN FILE: $Id: jamal.js 18 2007-06-13 09:07:32Z teemow $ */ +/** + * To quote Dave Cardwell: + * Built on the shoulders of giants: + * * John Resig - http://jquery.com/ + * + * Jamal : Javascript MVC Assembly Layout + * Copyright (c) 2007, Timo Derstappen + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright (c) 2007, Timo Derstappen + * @link + * @package jamal + * @subpackage jamal.session + * @since Jamal v 0.4 + * @version $Revision: 40 $ + * @modifiedby $LastChangedBy: teemow $ + * @lastmodified $Date: 2007-08-20 18:24:35 +0200 (Mo, 20. Aug 2007) $ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ + +/** + * Jamal session component + * + * This requires the modal component. + * + * @public + * @name jamal + * @cat session + */ +jamal.fn.extend({ + /* Constructor */ + /** + * Start the jamal session handling + * + * @example jamal.session(); + * @desc This starts the jamal session handling + * @public + * @name session + * @type Object + * @cat session + */ + session: function(){ + if(this.config.session) { + this.log('Session activated'); + this.session.active = true; + this.session.since = 0; + + jamal.ajaxSend(function(xhr){ + if(jamal.session.active){ + jamal.session.reset(); + } + }); + + jamal.ajaxSuccess(function(e, response){ + if(jamal.session.active){ + if(response.session_timeout) { + // session timeout + jamal.session.reload(); + } else { + jamal.session.reset(); + } + } + }); + + this.session.check(); + + return true; + } + return false; + } +}); + +jamal.fn.extend(jamal.fn.session, { + /** + * Flag for server-side session check + * + * @public + * @property + * @name jamal.session.active + * @type Boolean + * @cat session + */ + active: false, + + /** + * Minutes a session lasts. After this time the user gets logged out and + * is warned by a notification. + * + * @public + * @property + * @name jamal.session.timeout + * @type Number + * @cat session + */ + timeout: 30, // minutes + + /** + * Seconds between server-side session checks. + * + * @public + * @property + * @name jamal.session.freq + * @type Number + * @cat session + */ + freq: 60, // seconds + + /** + * This holds the time of the current session. + * + * @private + * @property + * @name jamal.session.since + * @type Number + * @cat session + */ + since: 0, // minutes + + /** + * Check the session on the server side + * + * @example jamal.session.check(); + * + * @private + * @name jamal.session.check + * @type Function + * @cat session + */ + check: function() { + if (this.since < this.timeout) { + if (this.since > 0) { + + var settings = { + url: '/session/', + type: 'GET', + dataType: 'json', + global: 'false', + success: function(response) { + if(jamal.session.active && response.session_timeout) { + jamal.session.destroy(); + } + } + }; + + jQuery.ajax(settings); + } + this.since += this.freq/60; + window.setTimeout("jamal.session.check();", this.freq*1000); + } else { + this.destroy(); + } + }, + + /** + * Kills a jamal session + * + * Calls the url /logout/ to kill the session on the server side. It is + * expected to get back some information to be displayed in a modal dialog + * + * @example jamal.session.destroy(); + * @desc Destroys the current session + * + * @private + * @name destroy + * @type Function + * @cat session + */ + destroy: function() { + this.active = false; + jamal.log('Session killed'); + + var settings = { + url: '/logout/', + type: 'GET', + dataType: 'json', + global: 'false', + success: function(response) { + if (response.redirect) { + jamal.session.redirect(); + } + + jamal.modal(response.content); + jamal.session.callback(); + } + }; + + jQuery.ajax(settings); + }, + + /** + * Empty callback to customize the login screen handling + * + * @example jamal.session.callback = function() { + * $('#login').focus(); + * $('form').form(function(response) { + * alert('logged in'); + * }); + * }; + * + * @public + * @name jamal.session.callback + * @type Function + * @cat session + */ + callback: function() { + return; + }, + + /** + * Reloads the current page + * + * @example jamal.session.reload(); + * @desc Loads the current page again (full request, no xhr) + * + * @public + * @name reload + * @type jamal + * @cat session + */ + reload: function() { + window.location.replace(window.location.href); + }, + + /** + * Reset jamal's session timer + * + * @example jamal.reset(); + * + * @private + * @name reset + * @type jamal + * @cat session + */ + reset: function() { + this.since = 0; + } +}); + +/* SVN FILE: $Id: startup.js 40 2007-08-20 16:24:35Z teemow $ */ +/** + * This file handles the jamal object creation when the dom has finished. + * + * jQuery is required + * + * Jamal : Javascript MVC Assembly Layout + * Copyright (c) 2006, Timo Derstappen + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright (c) 2006, Timo Derstappen + * @link + * @package jamal + * @subpackage jamal.startup + * @since Jamal v 0.1 + * @version $Revision: 40 $ + * @modifiedby $LastChangedBy: teemow $ + * @lastmodified $Date: 2007-08-20 18:24:35 +0200 (Mo, 20. Aug 2007) $ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ + +/** + * Jamal startup + * + * Mapping jamal to the shorter $j namespace and register the onload event to + * create an instance of jamal. + * + * This file sets the document.ready() function which is jQuery's replacement + * for the windows.onload event. + */ + +// Map over the $j in case of overwrite +if (typeof $j != "undefined") { + jamal._$j = $j; +} + +// Map the jamal namespace to '$j' +var $j = jamal; + +/** + * Window onload replacement of jquery + * + */ +$(function(){ + $j = jamal = jamal(); + $j.start(); +}); +/* + * Metadata - jQuery plugin for parsing metadata from elements + * + * Copyright (c) 2006 John Resig, Yehuda Katz, J�örn Zaefferer, Paul McLanahan + * + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + * Revision: $Id$ + * + */ + +/** + * Sets the type of metadata to use. Metadata is encoded in JSON, and each property + * in the JSON will become a property of the element itself. + * + * There are three supported types of metadata storage: + * + * attr: Inside an attribute. The name parameter indicates *which* attribute. + * + * class: Inside the class attribute, wrapped in curly braces: { } + * + * elem: Inside a child element (e.g. a script tag). The + * name parameter indicates *which* element. + * + * The metadata for an element is loaded the first time the element is accessed via jQuery. + * + * As a result, you can define the metadata type, use $(expr) to load the metadata into the elements + * matched by expr, then redefine the metadata type and run another $(expr) for other elements. + * + * @name $.metadata.setType + * + * @example

This is a p

+ * @before $.metadata.setType("class") + * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label" + * @desc Reads metadata from the class attribute + * + * @example

This is a p

+ * @before $.metadata.setType("attr", "data") + * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label" + * @desc Reads metadata from a "data" attribute + * + * @example

This is a p

+ * @before $.metadata.setType("elem", "script") + * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label" + * @desc Reads metadata from a nested script element + * + * @param String type The encoding type + * @param String name The name of the attribute to be used to get metadata (optional) + * @cat Plugins/Metadata + * @descr Sets the type of encoding to be used when loading metadata for the first time + * @type undefined + * @see metadata() + */ + +(function($) { + +$.extend({ + metadata : { + defaults : { + type: 'class', + name: 'metadata', + cre: /({.*})/, + single: 'metadata' + }, + setType: function( type, name ){ + this.defaults.type = type; + this.defaults.name = name; + }, + get: function( elem, opts ){ + var settings = $.extend({},this.defaults,opts); + // check for empty string in single property + if ( !settings.single.length ) settings.single = 'metadata'; + + var data = $.data(elem, settings.single); + // returned cached data if it already exists + if ( data ) return data; + + data = "{}"; + + if ( settings.type == "class" ) { + var m = settings.cre.exec( elem.className ); + if ( m ) + data = m[1]; + } else if ( settings.type == "elem" ) { + if( !elem.getElementsByTagName ) return; + var e = elem.getElementsByTagName(settings.name); + if ( e.length ) + data = $.trim(e[0].innerHTML); + } else if ( elem.getAttribute != undefined ) { + var attr = elem.getAttribute( settings.name ); + if ( attr ) + data = attr; + } + + if ( data.indexOf( '{' ) <0 ) + data = "{" + data + "}"; + + data = eval("(" + data + ")"); + + $.data( elem, settings.single, data ); + return data; + } + } +}); + +/** + * Returns the metadata object for the first member of the jQuery object. + * + * @name metadata + * @descr Returns element's metadata object + * @param Object opts An object contianing settings to override the defaults + * @type jQuery + * @cat Plugins/Metadata + */ +$.fn.metadata = function( opts ){ + return $.metadata.get( this[0], opts ); +}; + +})(jQuery);} +/* SVN FILE: $Id: jamal.js 18 2007-06-13 09:07:32Z teemow $ */ +/** + * To quote Dave Cardwell: + * Built on the shoulders of giants: + * * John Resig - http://jquery.com/ + * + * Jamal : Javascript MVC Assembly Layout + * Copyright (c) 2007, Timo Derstappen + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright (c) 2007, Timo Derstappen + * @link + * @package jamal + * @subpackage jamal.controller + * @since Jamal v 0.4 + * @version $Revision: 40 $ + * @modifiedby $LastChangedBy: teemow $ + * @lastmodified $Date: 2007-08-20 18:24:35 +0200 (Mo, 20. Aug 2007) $ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ + +/** + * Jamal app controller + * + * @public + * @cat controller + */ +jamal.extend(jamal.fn.c.prototype, { + components: ['session', 'modal'], + + beforeAction: function() { + }, + afterAction: function() { + } + +}); +/* SVN FILE: $Id: jamal.js 18 2007-06-13 09:07:32Z teemow $ */ +/** + * To quote Dave Cardwell: + * Built on the shoulders of giants: + * * John Resig - http://jquery.com/ + * + * Jamal : Javascript MVC Assembly Layout + * Copyright (c) 2007, Timo Derstappen + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright (c) 2007, Timo Derstappen + * @link + * @package jamal + * @subpackage jamal.model + * @since Jamal v 0.4 + * @version $Revision: 40 $ + * @modifiedby $LastChangedBy: teemow $ + * @lastmodified $Date: 2007-08-20 18:24:35 +0200 (Mo, 20. Aug 2007) $ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ + +/** + * Jamal app model + * + * @public + * @cat model + */ +jamal.extend(jamal.fn.m.prototype, { +}); +/* SVN FILE: $Id: jamal.js 18 2007-06-13 09:07:32Z teemow $ */ +/** + * To quote Dave Cardwell: + * Built on the shoulders of giants: + * * John Resig - http://jquery.com/ + * + * Jamal : Javascript MVC Assembly Layout + * Copyright (c) 2007, Timo Derstappen + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright (c) 2007, Timo Derstappen + * @link + * @package jamal + * @subpackage jamal.view + * @since Jamal v 0.4 + * @version $Revision: 40 $ + * @modifiedby $LastChangedBy: teemow $ + * @lastmodified $Date: 2007-08-20 18:24:35 +0200 (Mo, 20. Aug 2007) $ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ + +/** + * Jamal app view + * + * @public + * @cat view + */ +jamal.extend(jamal.fn.v.prototype, { +}); diff --git a/static/scripts/jquery.event.drag.js b/static/scripts/jquery.event.drag.js new file mode 100644 index 00000000000..e6ac8c8452e --- /dev/null +++ b/static/scripts/jquery.event.drag.js @@ -0,0 +1,126 @@ +/*! +jquery.event.drag.js ~ v1.4 ~ Copyright (c) 2008, Three Dub Media (http://threedubmedia.com) +Liscensed under the MIT License ~ http://threedubmedia.googlecode.com/files/MIT-LICENSE.txt +*/ +;(function($){ // secure $ jQuery alias +/*******************************************************************************************/ +// Created: 2008-06-04 | Updated: 2009-01-26 +/*******************************************************************************************/ +// Events: drag, dragstart, dragend +/*******************************************************************************************/ + +// jquery method +$.fn.drag = function( fn1, fn2, fn3 ){ + if ( fn2 ) this.bind('dragstart', fn1 ); // 2+ args + if ( fn3 ) this.bind('dragend', fn3 ); // 3 args + return !fn1 ? this.trigger('drag') // 0 args + : this.bind('drag', fn2 ? fn2 : fn1 ); // 1+ args + }; + +// local refs +var $event = $.event, $special = $event.special, + +// special event configuration +drag = $special.drag = { + not: ':input', // don't begin to drag on event.targets that match this selector + distance: 0, // distance dragged before dragstart + which: 1, // mouse button pressed to start drag sequence + setup: function( data ){ + data = $.extend({ + distance: drag.distance, + which: drag.which, + not: drag.not + }, data || {}); + data.distance = squared( data.distance ); // x² + y² = distance² + $event.add( this, "mousedown", handler, data ); + }, + teardown: function(){ + $event.remove( this, "mousedown", handler ); + if ( this === drag.dragging ) drag.dragging = drag.proxy = null; // deactivate element + selectable( this, true ); // enable text selection + } + }; + +// handle drag-releatd DOM events +function handler ( event ){ + var elem = this, returned, data = event.data || {}; + // mousemove or mouseup + if ( data.elem ){ + // update event properties... + elem = event.dragTarget = data.elem; // drag source element + event.dragProxy = drag.proxy || elem; // proxy element or source + event.cursorOffsetX = data.pageX - data.left; // mousedown offset + event.cursorOffsetY = data.pageY - data.top; // mousedown offset + event.offsetX = event.pageX - event.cursorOffsetX; // element offset + event.offsetY = event.pageY - event.cursorOffsetY; // element offset + } + // mousedown, check some initial props to avoid the switch statement + else if ( drag.dragging || ( data.which>0 && event.which!=data.which ) || + $( event.target ).is( data.not ) ) return; + // handle various events + switch ( event.type ){ + // mousedown, left click, event.target is not restricted, init dragging + case 'mousedown': + $.extend( data, $( elem ).offset(), { + elem: elem, target: event.target, + pageX: event.pageX, pageY: event.pageY + }); // store some initial attributes + $event.add( document, "mousemove mouseup", handler, data ); + selectable( elem, false ); // disable text selection + return false; // prevents text selection in safari + // mousemove, check distance, start dragging + case !drag.dragging && 'mousemove': + if ( squared( event.pageX-data.pageX ) + + squared( event.pageY-data.pageY ) // x² + y² = distance² + < data.distance ) break; // distance tolerance not reached + event.target = data.target; // force target from "mousedown" event (fix distance issue) + returned = hijack( event, "dragstart", elem ); // trigger "dragstart", return proxy element + if ( returned !== false ){ // "dragstart" not rejected + drag.dragging = elem; // activate element + drag.proxy = event.dragProxy = $( returned || elem )[0]; // set proxy + } + // mousemove, dragging + case 'mousemove': + if ( drag.dragging ){ + returned = hijack( event, "drag", elem ); // trigger "drag" + if ( $special.drop ){ // manage drop events + $special.drop.allowed = ( returned !== false ); // prevent drop + $special.drop.handler( event ); // "dropstart", "dropend" + } + if ( returned !== false ) break; // "drag" not rejected, stop + event.type = "mouseup"; // helps "drop" handler behave + } + // mouseup, stop dragging + case 'mouseup': + $event.remove( document, "mousemove mouseup", handler ); // remove page events + if ( drag.dragging ){ + if ( $special.drop ) $special.drop.handler( event ); // "drop" + hijack( event, "dragend", elem ); // trigger "dragend" + } + selectable( elem, true ); // enable text selection + drag.dragging = drag.proxy = data.elem = null; // deactivate element + break; + } + }; + +// set event type to custom value, and handle it +function hijack ( event, type, elem ){ + event.type = type; // force the event type + var result = $event.handle.call( elem, event ); + return result===false ? false : result || event.result; + }; + +// return the value squared +function squared ( value ){ return Math.pow( value, 2 ); }; + +// toggles text selection attributes +function selectable ( elem, bool ){ + if ( !elem ) return; // maybe element was removed ? + elem.unselectable = bool ? "off" : "on"; // IE + elem.onselectstart = function(){ return bool; }; // IE + if ( document.selection && document.selection.empty ) document.selection.empty(); // IE + if ( elem.style ) elem.style.MozUserSelect = bool ? "" : "none"; // FF + }; + +/*******************************************************************************************/ +})( jQuery ); // confine scope \ No newline at end of file diff --git a/static/scripts/jquery.js b/static/scripts/jquery.js index 88e661eec80..94e9c1755ef 100644 --- a/static/scripts/jquery.js +++ b/static/scripts/jquery.js @@ -1,34 +1,36 @@ -(function(){ -/* - * jQuery 1.2.6 - New Wave Javascript +/*! + * jQuery JavaScript Library v1.3.1 + * http://jquery.com/ * - * Copyright (c) 2008 John Resig (jquery.com) - * Dual licensed under the MIT (MIT-LICENSE.txt) - * and GPL (GPL-LICENSE.txt) licenses. + * Copyright (c) 2009 John Resig + * Dual licensed under the MIT and GPL licenses. + * http://docs.jquery.com/License * - * $Date: 2008-05-24 14:22:17 -0400 (Sat, 24 May 2008) $ - * $Rev: 5685 $ + * Date: 2009-01-21 20:42:16 -0500 (Wed, 21 Jan 2009) + * Revision: 6158 */ +(function(){ -// Map over jQuery in case of overwrite -var _jQuery = window.jQuery, -// Map over the $ in case of overwrite - _$ = window.$; +var + // Will speed up references to window, and allows munging its name. + window = this, + // Will speed up references to undefined, and allows munging its name. + undefined, + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + // Map over the $ in case of overwrite + _$ = window.$, -var jQuery = window.jQuery = window.$ = function( selector, context ) { - // The jQuery object is actually just the init constructor 'enhanced' - return new jQuery.fn.init( selector, context ); -}; + jQuery = window.jQuery = window.$ = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context ); + }, -// A simple way to check for HTML strings or ID strings -// (both of which we optimize for) -var quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/, - -// Is it a simple selector - isSimple = /^.[^:#\[\.]*$/, - -// Will speed up references to undefined, and allows munging its name. - undefined; + // A simple way to check for HTML strings or ID strings + // (both of which we optimize for) + quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/, + // Is it a simple selector + isSimple = /^.[^:#\[\.,]*$/; jQuery.fn = jQuery.prototype = { init: function( selector, context ) { @@ -39,10 +41,11 @@ jQuery.fn = jQuery.prototype = { if ( selector.nodeType ) { this[0] = selector; this.length = 1; + this.context = selector; return this; } // Handle HTML strings - if ( typeof selector == "string" ) { + if ( typeof selector === "string" ) { // Are we dealing with HTML string or an ID? var match = quickExpr.exec( selector ); @@ -57,17 +60,16 @@ jQuery.fn = jQuery.prototype = { else { var elem = document.getElementById( match[3] ); - // Make sure an element was located - if ( elem ){ - // Handle the case where IE and Opera return items - // by name instead of ID - if ( elem.id != match[3] ) - return jQuery().find( selector ); + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem && elem.id != match[3] ) + return jQuery().find( selector ); - // Otherwise, we inject the element directly into the jQuery object - return jQuery( elem ); - } - selector = []; + // Otherwise, we inject the element directly into the jQuery object + var ret = jQuery( elem || [] ); + ret.context = document; + ret.selector = selector; + return ret; } // HANDLE: $(expr, [context]) @@ -78,26 +80,32 @@ jQuery.fn = jQuery.prototype = { // HANDLE: $(function) // Shortcut for document ready } else if ( jQuery.isFunction( selector ) ) - return jQuery( document )[ jQuery.fn.ready ? "ready" : "load" ]( selector ); + return jQuery( document ).ready( selector ); + + // Make sure that old selector state is passed along + if ( selector.selector && selector.context ) { + this.selector = selector.selector; + this.context = selector.context; + } return this.setArray(jQuery.makeArray(selector)); }, + // Start with an empty selector + selector: "", + // The current version of jQuery being used - jquery: "1.2.6", + jquery: "1.3.1", // The number of elements contained in the matched element set size: function() { return this.length; }, - // The number of elements contained in the matched element set - length: 0, - // Get the Nth element in the matched element set OR // Get the whole matched element set as a clean array get: function( num ) { - return num == undefined ? + return num === undefined ? // Return a 'clean' array jQuery.makeArray( this ) : @@ -108,13 +116,20 @@ jQuery.fn = jQuery.prototype = { // Take an array of elements and push it onto the stack // (returning the new matched element set) - pushStack: function( elems ) { + pushStack: function( elems, name, selector ) { // Build a new jQuery matched element set var ret = jQuery( elems ); // Add the old object onto the stack (as a reference) ret.prevObject = this; + ret.context = this.context; + + if ( name === "find" ) + ret.selector = this.selector + (this.selector ? " " : "") + selector; + else if ( name ) + ret.selector = this.selector + "." + name + "(" + selector + ")"; + // Return the newly-formed element set return ret; }, @@ -141,8 +156,6 @@ jQuery.fn = jQuery.prototype = { // Determine the position of an element within // the matched set of elements index: function( elem ) { - var ret = -1; - // Locate the position of the desired element return jQuery.inArray( // If it receives a jQuery object, the first element is used @@ -154,7 +167,7 @@ jQuery.fn = jQuery.prototype = { var options = name; // Look for the case where we're accessing a style value - if ( name.constructor == String ) + if ( typeof name === "string" ) if ( value === undefined ) return this[0] && jQuery[ type || "attr" ]( this[0], name ); @@ -184,7 +197,7 @@ jQuery.fn = jQuery.prototype = { }, text: function( text ) { - if ( typeof text != "object" && text != null ) + if ( typeof text !== "object" && text != null ) return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) ); var ret = ""; @@ -202,20 +215,22 @@ jQuery.fn = jQuery.prototype = { }, wrapAll: function( html ) { - if ( this[0] ) + if ( this[0] ) { // The elements to wrap the target around - jQuery( html, this[0].ownerDocument ) - .clone() - .insertBefore( this[0] ) - .map(function(){ - var elem = this; + var wrap = jQuery( html, this[0].ownerDocument ).clone(); - while ( elem.firstChild ) - elem = elem.firstChild; + if ( this[0].parentNode ) + wrap.insertBefore( this[0] ); - return elem; - }) - .append(this); + wrap.map(function(){ + var elem = this; + + while ( elem.firstChild ) + elem = elem.firstChild; + + return elem; + }).append(this); + } return this; }, @@ -233,27 +248,27 @@ jQuery.fn = jQuery.prototype = { }, append: function() { - return this.domManip(arguments, true, false, function(elem){ + return this.domManip(arguments, true, function(elem){ if (this.nodeType == 1) this.appendChild( elem ); }); }, prepend: function() { - return this.domManip(arguments, true, true, function(elem){ + return this.domManip(arguments, true, function(elem){ if (this.nodeType == 1) this.insertBefore( elem, this.firstChild ); }); }, before: function() { - return this.domManip(arguments, false, false, function(elem){ + return this.domManip(arguments, false, function(elem){ this.parentNode.insertBefore( elem, this ); }); }, after: function() { - return this.domManip(arguments, false, true, function(elem){ + return this.domManip(arguments, false, function(elem){ this.parentNode.insertBefore( elem, this.nextSibling ); }); }, @@ -262,20 +277,31 @@ jQuery.fn = jQuery.prototype = { return this.prevObject || jQuery( [] ); }, - find: function( selector ) { - var elems = jQuery.map(this, function(elem){ - return jQuery.find( selector, elem ); - }); + // For internal use only. + // Behaves like an Array's .push method, not like a jQuery method. + push: [].push, - return this.pushStack( /[^+>] [^+>]/.test( selector ) || selector.indexOf("..") > -1 ? - jQuery.unique( elems ) : - elems ); + find: function( selector ) { + if ( this.length === 1 && !/,/.test(selector) ) { + var ret = this.pushStack( [], "find", selector ); + ret.length = 0; + jQuery.find( selector, this[0], ret ); + return ret; + } else { + var elems = jQuery.map(this, function(elem){ + return jQuery.find( selector, elem ); + }); + + return this.pushStack( /[^+>] [^+>]/.test( selector ) ? + jQuery.unique( elems ) : + elems, "find", selector ); + } }, clone: function( events ) { // Do the clone var ret = this.map(function(){ - if ( jQuery.browser.msie && !jQuery.isXMLDoc(this) ) { + if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) { // IE copies events bound via attachEvent when // using cloneNode. Calling detachEvent on the // clone will also remove the events from the orignal @@ -296,7 +322,7 @@ jQuery.fn = jQuery.prototype = { // removeData doesn't work here, IE removes it from the original as well // this is primarily for IE but the data expando shouldn't be copied over in any browser var clone = ret.find("*").andSelf().each(function(){ - if ( this[ expando ] != undefined ) + if ( this[ expando ] !== undefined ) this[ expando ] = null; }); @@ -323,14 +349,29 @@ jQuery.fn = jQuery.prototype = { return selector.call( elem, i ); }) || - jQuery.multiFilter( selector, this ) ); + jQuery.multiFilter( selector, jQuery.grep(this, function(elem){ + return elem.nodeType === 1; + }) ), "filter", selector ); + }, + + closest: function( selector ) { + var pos = jQuery.expr.match.POS.test( selector ) ? jQuery(selector) : null; + + return this.map(function(){ + var cur = this; + while ( cur && cur.ownerDocument ) { + if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selector) ) + return cur; + cur = cur.parentNode; + } + }); }, not: function( selector ) { - if ( selector.constructor == String ) + if ( typeof selector === "string" ) // test special case where just one selector is passed in if ( isSimple.test( selector ) ) - return this.pushStack( jQuery.multiFilter( selector, this, true ) ); + return this.pushStack( jQuery.multiFilter( selector, this, true ), "not", selector ); else selector = jQuery.multiFilter( selector, this ); @@ -343,7 +384,7 @@ jQuery.fn = jQuery.prototype = { add: function( selector ) { return this.pushStack( jQuery.unique( jQuery.merge( this.get(), - typeof selector == 'string' ? + typeof selector === "string" ? jQuery( selector ) : jQuery.makeArray( selector ) ))); @@ -354,15 +395,17 @@ jQuery.fn = jQuery.prototype = { }, hasClass: function( selector ) { - return this.is( "." + selector ); + return !!selector && this.is( "." + selector ); }, val: function( value ) { - if ( value == undefined ) { - - if ( this.length ) { - var elem = this[0]; + if ( value === undefined ) { + var elem = this[0]; + if ( elem ) { + if( jQuery.nodeName( elem, 'option' ) ) + return (elem.attributes.value || {}).specified ? elem.value : elem.text; + // We need to handle select boxes special if ( jQuery.nodeName( elem, "select" ) ) { var index = elem.selectedIndex, @@ -380,7 +423,7 @@ jQuery.fn = jQuery.prototype = { if ( option.selected ) { // Get the specifc value for the option - value = jQuery.browser.msie && !option.attributes.value.specified ? option.text : option.value; + value = jQuery(option).val(); // We don't need an array for one selects if ( one ) @@ -391,25 +434,25 @@ jQuery.fn = jQuery.prototype = { } } - return values; + return values; + } // Everything else, we just grab the value - } else - return (this[0].value || "").replace(/\r/g, ""); + return (elem.value || "").replace(/\r/g, ""); } return undefined; } - if( value.constructor == Number ) + if ( typeof value === "number" ) value += ''; return this.each(function(){ if ( this.nodeType != 1 ) return; - if ( value.constructor == Array && /radio|checkbox/.test( this.type ) ) + if ( jQuery.isArray(value) && /radio|checkbox/.test( this.type ) ) this.checked = (jQuery.inArray(this.value, value) >= 0 || jQuery.inArray(this.name, value) >= 0); @@ -430,7 +473,7 @@ jQuery.fn = jQuery.prototype = { }, html: function( value ) { - return value == undefined ? + return value === undefined ? (this[0] ? this[0].innerHTML : null) : @@ -442,11 +485,12 @@ jQuery.fn = jQuery.prototype = { }, eq: function( i ) { - return this.slice( i, i + 1 ); + return this.slice( i, +i + 1 ); }, slice: function() { - return this.pushStack( Array.prototype.slice.apply( this, arguments ) ); + return this.pushStack( Array.prototype.slice.apply( this, arguments ), + "slice", Array.prototype.slice.call(arguments).join(",") ); }, map: function( callback ) { @@ -459,69 +503,29 @@ jQuery.fn = jQuery.prototype = { return this.add( this.prevObject ); }, - data: function( key, value ){ - var parts = key.split("."); - parts[1] = parts[1] ? "." + parts[1] : ""; + domManip: function( args, table, callback ) { + if ( this[0] ) { + var fragment = (this[0].ownerDocument || this[0]).createDocumentFragment(), + scripts = jQuery.clean( args, (this[0].ownerDocument || this[0]), fragment ), + first = fragment.firstChild, + extra = this.length > 1 ? fragment.cloneNode(true) : fragment; - if ( value === undefined ) { - var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]); + if ( first ) + for ( var i = 0, l = this.length; i < l; i++ ) + callback.call( root(this[i], first), i > 0 ? extra.cloneNode(true) : fragment ); + + if ( scripts ) + jQuery.each( scripts, evalScript ); + } - if ( data === undefined && this.length ) - data = jQuery.data( this[0], key ); - - return data === undefined && parts[1] ? - this.data( parts[0] ) : - data; - } else - return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){ - jQuery.data( this, key, value ); - }); - }, - - removeData: function( key ){ - return this.each(function(){ - jQuery.removeData( this, key ); - }); - }, - - domManip: function( args, table, reverse, callback ) { - var clone = this.length > 1, elems; - - return this.each(function(){ - if ( !elems ) { - elems = jQuery.clean( args, this.ownerDocument ); - - if ( reverse ) - elems.reverse(); - } - - var obj = this; - - if ( table && jQuery.nodeName( this, "table" ) && jQuery.nodeName( elems[0], "tr" ) ) - obj = this.getElementsByTagName("tbody")[0] || this.appendChild( this.ownerDocument.createElement("tbody") ); - - var scripts = jQuery( [] ); - - jQuery.each(elems, function(){ - var elem = clone ? - jQuery( this ).clone( true )[0] : - this; - - // execute all scripts after the elements have been injected - if ( jQuery.nodeName( elem, "script" ) ) - scripts = scripts.add( elem ); - else { - // Remove any inner scripts for later evaluation - if ( elem.nodeType == 1 ) - scripts = scripts.add( jQuery( "script", elem ).remove() ); - - // Inject the elements into the document - callback.call( obj, elem ); - } - }); - - scripts.each( evalScript ); - }); + return this; + + function root( elem, cur ) { + return table && jQuery.nodeName(elem, "table") && jQuery.nodeName(cur, "tr") ? + (elem.getElementsByTagName("tbody")[0] || + elem.appendChild(elem.ownerDocument.createElement("tbody"))) : + elem; + } } }; @@ -552,7 +556,7 @@ jQuery.extend = jQuery.fn.extend = function() { var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options; // Handle a deep copy situation - if ( target.constructor == Boolean ) { + if ( typeof target === "boolean" ) { deep = target; target = arguments[1] || {}; // skip the boolean and the target @@ -560,7 +564,7 @@ jQuery.extend = jQuery.fn.extend = function() { } // Handle case when target is a string or something (possible in deep copy) - if ( typeof target != "object" && typeof target != "function" ) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) target = {}; // extend jQuery itself if only one argument is passed @@ -581,7 +585,7 @@ jQuery.extend = jQuery.fn.extend = function() { continue; // Recurse if we're merging object values - if ( deep && copy && typeof copy == "object" && !copy.nodeType ) + if ( deep && copy && typeof copy === "object" && !copy.nodeType ) target[ name ] = jQuery.extend( deep, // Never move original objects, clone them src || ( copy.length != null ? [ ] : { } ) @@ -597,11 +601,11 @@ jQuery.extend = jQuery.fn.extend = function() { return target; }; -var expando = "jQuery" + now(), uuid = 0, windowData = {}, - // exclude the following css properties to add px - exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i, +// exclude the following css properties to add px +var exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i, // cache defaultView - defaultView = document.defaultView || {}; + defaultView = document.defaultView || {}, + toString = Object.prototype.toString; jQuery.extend({ noConflict: function( deep ) { @@ -613,16 +617,21 @@ jQuery.extend({ return jQuery; }, - // See test/unit/core.js for details concerning this function. - isFunction: function( fn ) { - return !!fn && typeof fn != "string" && !fn.nodeName && - fn.constructor != Array && /^[\s[]?function/.test( fn + "" ); + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return toString.call(obj) === "[object Function]"; + }, + + isArray: function( obj ) { + return toString.call(obj) === "[object Array]"; }, // check if an element is in a (or is an) XML document isXMLDoc: function( elem ) { - return elem.documentElement && !elem.body || - elem.tagName && elem.ownerDocument && !elem.ownerDocument.body; + return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" || + !!elem.ownerDocument && jQuery.isXMLDoc( elem.ownerDocument ); }, // Evalulates a script in a global context @@ -636,10 +645,10 @@ jQuery.extend({ script = document.createElement("script"); script.type = "text/javascript"; - if ( jQuery.browser.msie ) - script.text = data; - else + if ( jQuery.support.scriptEval ) script.appendChild( document.createTextNode( data ) ); + else + script.text = data; // Use insertBefore instead of appendChild to circumvent an IE6 bug. // This arises when a base node is used (#2709). @@ -652,80 +661,12 @@ jQuery.extend({ return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase(); }, - cache: {}, - - data: function( elem, name, data ) { - elem = elem == window ? - windowData : - elem; - - var id = elem[ expando ]; - - // Compute a unique ID for the element - if ( !id ) - id = elem[ expando ] = ++uuid; - - // Only generate the data cache if we're - // trying to access or manipulate it - if ( name && !jQuery.cache[ id ] ) - jQuery.cache[ id ] = {}; - - // Prevent overriding the named cache with undefined values - if ( data !== undefined ) - jQuery.cache[ id ][ name ] = data; - - // Return the named cache data, or the ID for the element - return name ? - jQuery.cache[ id ][ name ] : - id; - }, - - removeData: function( elem, name ) { - elem = elem == window ? - windowData : - elem; - - var id = elem[ expando ]; - - // If we want to remove a specific section of the element's data - if ( name ) { - if ( jQuery.cache[ id ] ) { - // Remove the section of cache data - delete jQuery.cache[ id ][ name ]; - - // If we've removed all the data, remove the element's cache - name = ""; - - for ( name in jQuery.cache[ id ] ) - break; - - if ( !name ) - jQuery.removeData( elem ); - } - - // Otherwise, we want to remove all of the element's data - } else { - // Clean up the element expando - try { - delete elem[ expando ]; - } catch(e){ - // IE has trouble directly removing the expando - // but it's ok with using removeAttribute - if ( elem.removeAttribute ) - elem.removeAttribute( expando ); - } - - // Completely remove the data cache - delete jQuery.cache[ id ]; - } - }, - // args is for internal usage only each: function( object, callback, args ) { var name, i = 0, length = object.length; if ( args ) { - if ( length == undefined ) { + if ( length === undefined ) { for ( name in object ) if ( callback.apply( object[ name ], args ) === false ) break; @@ -736,7 +677,7 @@ jQuery.extend({ // A special, fast, case for the most common use of each } else { - if ( length == undefined ) { + if ( length === undefined ) { for ( name in object ) if ( callback.call( object[ name ], name, object[ name ] ) === false ) break; @@ -754,7 +695,7 @@ jQuery.extend({ value = value.call( elem, i ); // Handle passing in a number to a CSS property - return value && value.constructor == Number && type == "curCSS" && !exclude.test( name ) ? + return typeof value === "number" && type == "curCSS" && !exclude.test( name ) ? value + "px" : value; }, @@ -771,7 +712,7 @@ jQuery.extend({ // internal only, use removeClass("class") remove: function( elem, classNames ) { if (elem.nodeType == 1) - elem.className = classNames != undefined ? + elem.className = classNames !== undefined ? jQuery.grep(elem.className.split(/\s+/), function(className){ return !jQuery.className.has( classNames, className ); }).join(" ") : @@ -780,7 +721,7 @@ jQuery.extend({ // internal only, use hasClass("class") has: function( elem, className ) { - return jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1; + return elem && jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1; } }, @@ -828,30 +769,14 @@ jQuery.extend({ curCSS: function( elem, name, force ) { var ret, style = elem.style; - // A helper method for determining if an element's values are broken - function color( elem ) { - if ( !jQuery.browser.safari ) - return false; - - // defaultView is cached - var ret = defaultView.getComputedStyle( elem, null ); - return !ret || ret.getPropertyValue("color") == ""; - } - // We need to handle opacity special in IE - if ( name == "opacity" && jQuery.browser.msie ) { + if ( name == "opacity" && !jQuery.support.opacity ) { ret = jQuery.attr( style, "opacity" ); return ret == "" ? "1" : ret; } - // Opera sometimes will give the wrong display answer, this fixes it, see #2037 - if ( jQuery.browser.opera && name == "display" ) { - var save = style.outline; - style.outline = "0 solid black"; - style.outline = save; - } // Make sure we're using the right name for getting the float value if ( name.match( /float/i ) ) @@ -870,38 +795,9 @@ jQuery.extend({ var computedStyle = defaultView.getComputedStyle( elem, null ); - if ( computedStyle && !color( elem ) ) + if ( computedStyle ) ret = computedStyle.getPropertyValue( name ); - // If the element isn't reporting its values properly in Safari - // then some display: none elements are involved - else { - var swap = [], stack = [], a = elem, i = 0; - - // Locate all of the parent display: none elements - for ( ; a && color(a); a = a.parentNode ) - stack.unshift(a); - - // Go through and make them visible, but in reverse - // (It would be better if we knew the exact display type that they had) - for ( ; i < stack.length; i++ ) - if ( color( stack[ i ] ) ) { - swap[ i ] = stack[ i ].style.display; - stack[ i ].style.display = "block"; - } - - // Since we flip the display style, we have to handle that - // one special, otherwise get the value - ret = name == "display" && swap[ stack.length - 1 ] != null ? - "none" : - ( computedStyle && computedStyle.getPropertyValue( name ) ) || ""; - - // Finally, revert the display styles back - for ( i = 0; i < swap.length; i++ ) - if ( swap[ i ] != null ) - stack[ i ].style.display = swap[ i ]; - } - // We should always get a number back from opacity if ( name == "opacity" && ret == "" ) ret = "1"; @@ -936,22 +832,32 @@ jQuery.extend({ return ret; }, - clean: function( elems, context ) { - var ret = []; + clean: function( elems, context, fragment ) { context = context || document; + // !context.createElement fails in IE with an error but returns typeof 'object' - if (typeof context.createElement == 'undefined') + if ( typeof context.createElement === "undefined" ) context = context.ownerDocument || context[0] && context[0].ownerDocument || document; + // If a single string is passed in and it's a single tag + // just do a createElement and skip the rest + if ( !fragment && elems.length === 1 && typeof elems[0] === "string" ) { + var match = /^<(\w+)\s*\/?>$/.exec(elems[0]); + if ( match ) + return [ context.createElement( match[1] ) ]; + } + + var ret = [], scripts = [], div = context.createElement("div"); + jQuery.each(elems, function(i, elem){ + if ( typeof elem === "number" ) + elem += ''; + if ( !elem ) return; - if ( elem.constructor == Number ) - elem += ''; - // Convert html string into DOM nodes - if ( typeof elem == "string" ) { + if ( typeof elem === "string" ) { // Fix "XHTML"-style tags in all browsers elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){ return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ? @@ -960,7 +866,7 @@ jQuery.extend({ }); // Trim whitespace, otherwise indexOf won't work as expected - var tags = jQuery.trim( elem ).toLowerCase(), div = context.createElement("div"); + var tags = jQuery.trim( elem ).toLowerCase(); var wrap = // option or optgroup @@ -984,7 +890,7 @@ jQuery.extend({ [ 2, "", "
" ] || // IE can't serialize and + + + + + + + + + + + + + + + + +
+
+
+
+

db:

+ + +
+
+ + + +
+
+
+
+
+
+
+ +
+
+
+
+
    +
  • +
    +
    +
    mRNA
    +
    +
    +
    + +
    +
    +
    +
  • +
+
+
+
    +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
+
+
+
+
+
+

History

+
+
+ + + +
+
+
    +
  • +
    +
    + + 3: UCSC Main on Human: knownGene (genome) + +
    +
  • +
+
+ + diff --git a/universe_wsgi.ini.sample b/universe_wsgi.ini.sample index ab89ce4c554..14b003fdfd3 100644 --- a/universe_wsgi.ini.sample +++ b/universe_wsgi.ini.sample @@ -40,6 +40,9 @@ tool_data_path = tool-data datatype_converters_config_file = datatype_converters_conf.xml datatype_converters_path = %(here)s/lib/galaxy/datatypes/converters +# Datatype indexers +datatype_indexers_path = %(here)s/lib/galaxy/datatypes/indexers + # Session support (beaker) use_beaker_session = True session_type = file