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('');
+
+ 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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+ 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