galaxy-dev
Threads by month
- ----- 2026 -----
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- 10009 discussions
details: http://www.bx.psu.edu/hg/galaxy/rev/5536d57237a3
changeset: 3626:5536d57237a3
user: Kanwei Li <kanwei(a)gmail.com>
date: Fri Apr 09 18:09:54 2010 -0400
description:
- Update jQuery.jStore, JSON2
- Fix issue with all local prefs being deleted on "collapse all"
- Refactored history display code into galaxy.base
diffstat:
static/jStore.Flash.html | 19 -
static/jStore.swf | 0
static/scripts/class.js | 80 +
static/scripts/galaxy.base.js | 87 +
static/scripts/jquery.jstore-all.js | 748 -----------
static/scripts/jquery.jstore.js | 1810 ++++++++++++++++++++++++++++
static/scripts/json2.js | 26 +-
static/scripts/packed/class.js | 7 +
static/scripts/packed/galaxy.base.js | 2 +-
static/scripts/packed/jquery.jstore-all.js | 1 -
static/scripts/packed/jquery.jstore.js | 17 +
static/scripts/packed/json2.js | 2 +-
templates/display_base.mako | 29 +-
templates/history/display.mako | 264 +---
templates/history/view.mako | 259 +---
templates/page/display.mako | 272 +---
templates/root/history.mako | 222 +--
templates/workflow/editor.mako | 42 +-
18 files changed, 2216 insertions(+), 1671 deletions(-)
diffs (truncated from 4199 to 3000 lines):
diff -r a9402fc7ce1b -r 5536d57237a3 static/jStore.Flash.html
--- a/static/jStore.Flash.html Fri Apr 09 13:02:15 2010 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,19 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
- <head>
- <title>Flash External Object</title>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
- <script type="text/javascript">
- /**
- * This function captures the flash_ready event. We need to relay this
- * back to the parent so it knows flash is ready.
- */
- function flash_ready(){
- parent.flash_ready();
- }
- </script>
- </head>
- <body>
- <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#versiā¦" width="1" height="1" id="jStoreFlash"><param name="allowScriptAccess" value="always" /><param name="movie" value="jStore.swf" /><param name="quality" value="high" /><param name="bgcolor" value="#ffcc00" /><embed src="jStore.swf" quality="high" bgcolor="#ffcc00" width="1" height="1" name="jStoreFlash" align="middle" allowScriptAccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" /></object>
- </body>
-</html>
\ No newline at end of file
diff -r a9402fc7ce1b -r 5536d57237a3 static/jStore.swf
Binary file static/jStore.swf has changed
diff -r a9402fc7ce1b -r 5536d57237a3 static/scripts/class.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/static/scripts/class.js Fri Apr 09 18:09:54 2010 -0400
@@ -0,0 +1,80 @@
+/*!
+ * Class definition
+ *
+ * Copyright (c) 2008 John Resig (http://ejohn.org/blog/simple-javascript-inheritance/)
+ * Inspired by base2 and Prototype
+ */
+
+"use strict";
+
+(function () {
+
+ var initializing = false,
+
+ fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
+
+ // The base Class implementation (does nothing)
+ this.Class = function(){};
+
+ // Create a new Class that inherits from this class
+ Class.extend = function (prop)
+ {
+ var _super = this.prototype;
+
+ // Instantiate a base class (but only create the instance, don't run the init constructor)
+ initializing = true;
+
+ var prototype = new this();
+
+ initializing = false;
+
+ // Copy the properties over onto the new prototype
+ for (var name in prop)
+ {
+
+ // Check if we're overwriting an existing function
+ prototype[name] = (typeof prop[name] === "function" && typeof _super[name] === "function" && fnTest.test(prop[name]) ?
+
+ (function(name, fn)
+ {
+ return function()
+ {
+ var tmp = this._super;
+
+ // Add a new ._super() method that is the same method
+ // but on the super-class
+ this._super = _super[name];
+
+ // The method only need to be bound temporarily, so we
+ // remove it when we're done executing
+ var ret = fn.apply(this, arguments);
+ this._super = tmp;
+
+ return ret;
+ };
+ }(name, prop[name])) : prop[name]);
+ }
+
+ // The dummy class constructor
+ function Class()
+ {
+ // All construction is actually done in the init method
+ if (!initializing && this.init)
+ {
+ this.init.apply(this, arguments);
+ }
+ }
+
+ // Populate our constructed prototype object
+ Class.prototype = prototype;
+
+ // Enforce the constructor to be what we expect
+ Class.constructor = Class;
+
+ // And make this class extendable
+ Class.extend = arguments.callee;
+
+ return Class;
+ };
+
+}());
diff -r a9402fc7ce1b -r 5536d57237a3 static/scripts/galaxy.base.js
--- a/static/scripts/galaxy.base.js Fri Apr 09 13:02:15 2010 -0400
+++ b/static/scripts/galaxy.base.js Fri Apr 09 18:09:54 2010 -0400
@@ -275,6 +275,93 @@
});
}
+function init_history_items(historywrapper, noinit) {
+
+ var action = function() {
+ // Load saved state and show as necessary
+ try {
+ var stored = $.jStore.store("history_expand_state");
+ if (stored) {
+ for (var id in stored) {
+ $("#" + id + " div.historyItemBody" ).show();
+ }
+ }
+ } catch(err) {
+ // Something was wrong with values in storage, so clear storage
+ $.jStore.remove("history_expand_state");
+ }
+
+ // If Mozilla, hide scrollbars in hidden items since they cause animation bugs
+ if ( $.browser.mozilla ) {
+ $( "div.historyItemBody" ).each( function() {
+ if ( ! $(this).is( ":visible" ) ) $(this).find( "pre.peek" ).css( "overflow", "hidden" );
+ })
+ }
+
+ historywrapper.each( function() {
+ var id = this.id;
+ var body = $(this).children( "div.historyItemBody" );
+ var peek = body.find( "pre.peek" )
+ $(this).children( ".historyItemTitleBar" ).find( ".historyItemTitle" ).wrap( "<a href='#'></a>" ).click( function() {
+ if ( body.is(":visible") ) {
+ // Hiding stuff here
+ if ( $.browser.mozilla ) { peek.css( "overflow", "hidden" ) }
+ body.slideUp( "fast" );
+
+ if (!noinit) { // Ignore embedded item actions
+ // Save setting
+ var prefs = $.jStore.store("history_expand_state");
+ if (prefs) {
+ delete prefs[id];
+ $.jStore.store("history_expand_state", prefs);
+ }
+ }
+ } else {
+ // Showing stuff here
+ body.slideDown( "fast", function() {
+ if ( $.browser.mozilla ) { peek.css( "overflow", "auto" ); }
+ });
+
+ if (!noinit) {
+ // Save setting
+ var prefs = $.jStore.store("history_expand_state");
+ if (prefs === undefined) { prefs = {}; }
+ prefs[id] = true;
+ $.jStore.store("history_expand_state", prefs);
+ }
+ }
+ return false;
+ });
+ });
+
+ // Generate 'collapse all' link
+ $("#top-links > a.toggle").click( function() {
+ var prefs = $.jStore.store("history_expand_state");
+ if (prefs === undefined) { prefs = {}; }
+ $( "div.historyItemBody:visible" ).each( function() {
+ if ( $.browser.mozilla ) {
+ $(this).find( "pre.peek" ).css( "overflow", "hidden" );
+ }
+ $(this).slideUp( "fast" );
+ if (prefs) {
+ delete prefs[$(this).parent().attr("id")];
+ }
+ });
+ $.jStore.store("history_expand_state", prefs);
+ }).show();
+ }
+
+ if (noinit) {
+ action();
+ } else {
+ // Load jStore for local storage
+ $.jStore.init("galaxy"); // Auto-select best storage
+ $.jStore.engineReady(function() {
+ action();
+ });
+ }
+}
+
$(document).ready( function() {
// Links with confirmation
$( "a[confirm]" ).click( function() {
diff -r a9402fc7ce1b -r 5536d57237a3 static/scripts/jquery.jstore-all.js
--- a/static/scripts/jquery.jstore-all.js Fri Apr 09 13:02:15 2010 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,748 +0,0 @@
-/*
- * jStore - Persistent Client-Side Storage
- *
- * Copyright (c) 2009 Eric Garside (http://eric.garside.name)
- *
- * Dual licensed under:
- * MIT: http://www.opensource.org/licenses/mit-license.php
- * GPLv3: http://www.opensource.org/licenses/gpl-3.0.html
- *//**
- * Javascript Class Framework
- *
- * Copyright (c) 2008 John Resig (http://ejohn.org/blog/simple-javascript-inheritance/)
- * Inspired by base2 and Prototype
- */
-(function(){
- var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
-
- // The base Class implementation (does nothing)
- this.Class = function(){};
-
- // Create a new Class that inherits from this class
- Class.extend = function(prop) {
- var _super = this.prototype;
-
- // Instantiate a base class (but only create the instance,
- // don't run the init constructor)
- initializing = true;
- var prototype = new this();
- initializing = false;
-
- // Copy the properties over onto the new prototype
- for (var name in prop) {
- // Check if we're overwriting an existing function
- prototype[name] = typeof prop[name] == "function" &&
- typeof _super[name] == "function" && fnTest.test(prop[name]) ?
- (function(name, fn){
- return function() {
- var tmp = this._super;
-
- // Add a new ._super() method that is the same method
- // but on the super-class
- this._super = _super[name];
-
- // The method only need to be bound temporarily, so we
- // remove it when we're done executing
- var ret = fn.apply(this, arguments);
- this._super = tmp;
-
- return ret;
- };
- })(name, prop[name]) :
- prop[name];
- }
-
- // The dummy class constructor
- function Class() {
- // All construction is actually done in the init method
- if ( !initializing && this.init )
- this.init.apply(this, arguments);
- }
-
- // Populate our constructed prototype object
- Class.prototype = prototype;
-
- // Enforce the constructor to be what we expect
- Class.constructor = Class;
-
- // And make this class extendable
- Class.extend = arguments.callee;
-
- return Class;
- };
-})();
-/*
- * jStore Delegate Framework
- * Copyright (c) 2009 Eric Garside (http://eric.garside.name)
- */
-(function($){
-
- this.jStoreDelegate = Class.extend({
- init: function(parent){
- // The Object this delgate operates for
- this.parent = parent;
- // Container for callbacks to dispatch.
- // eventType => [ callback, callback, ... ]
- this.callbacks = {};
- },
- bind: function(event, callback){
- if ( !$.isFunction(callback) ) return this;
- if ( !this.callbacks[ event ] ) this.callbacks[ event ] = [];
-
- this.callbacks[ event ].push(callback);
-
- return this;
- },
- trigger: function(){
- var parent = this.parent,
- args = [].slice.call(arguments),
- event = args.shift(),
- handlers = this.callbacks[ event ];
-
- if ( !handlers ) return false;
-
- $.each(handlers, function(){ this.apply(parent, args) });
- return this;
- }
- });
-
-})(jQuery);/**
- * jStore-jQuery Interface
- * Copyright (c) 2009 Eric Garside (http://eric.garside.name)
- */
-(function($){
-
- // Setup the jStore namespace in jQuery for options storage
- $.jStore = {};
-
- // Seed the options in
- $.extend($.jStore, {
- EngineOrder: [],
- // Engines should put their availability tests within jStore.Availability
- Availability: {},
- // Defined engines should enter themselves into the jStore.Engines
- Engines: {},
- // Instanciated engines should exist within jStore.Instances
- Instances: {},
- // The current engine to use for storage
- CurrentEngine: null,
- // Provide global settings for overwriting
- defaults: {
- project: null,
- engine: null,
- autoload: true,
- flash: 'jStore.Flash.html'
- },
- // Boolean for ready state handling
- isReady: false,
- // Boolean for flash ready state handling
- isFlashReady: false,
- // An event delegate
- delegate: new jStoreDelegate($.jStore)
- .bind('jStore-ready', function(engine){
- $.jStore.isReady = true;
- if ($.jStore.defaults.autoload) engine.connect();
- })
- .bind('flash-ready', function(){
- $.jStore.isFlashReady = true;
- })
- });
-
- // Enable ready callback for jStore
- $.jStore.ready = function(callback){
- if ($.jStore.isReady) callback.apply($.jStore, [$.jStore.CurrentEngine]);
- else $.jStore.delegate.bind('jStore-ready', callback);
- }
-
- // Enable failure callback registration for jStore
- $.jStore.fail = function(callback){
- $.jStore.delegate.bind('jStore-failure', callback);
- }
-
- // Enable ready callback for Flash
- $.jStore.flashReady = function(callback){
- if ($.jStore.isFlashReady) callback.apply($.jStore, [$.jStore.CurrentEngine]);
- else $.jStore.delegate.bind('flash-ready', callback);
- }
-
- // Enable and test an engine
- $.jStore.use = function(engine, project, identifier){
- project = project || $.jStore.defaults.project || location.hostname.replace(/\./g, '-') || 'unknown';
-
- var e = $.jStore.Engines[engine.toLowerCase()] || null,
- name = (identifier ? identifier + '.' : '') + project + '.' + engine;
-
- if ( !e ) throw 'JSTORE_ENGINE_UNDEFINED';
-
- // Instanciate the engine
- e = new e(project, name);
-
- // Prevent against naming conflicts
- if ($.jStore.Instances[name]) throw 'JSTORE_JRI_CONFLICT';
-
- // Test the engine
- if (e.isAvailable()){
- $.jStore.Instances[name] = e; // The Easy Way
- if (!$.jStore.CurrentEngine){
- $.jStore.CurrentEngine = e;
- }
- $.jStore.delegate.trigger('jStore-ready', e);
- } else {
- if (!e.autoload) // Not available
- throw 'JSTORE_ENGINE_UNAVILABLE';
- else { // The hard way
- e.included(function(){
- if (this.isAvailable()) { // Worked out
- $.jStore.Instances[name] = this;
- // If there is no current engine, use this one
- if (!$.jStore.CurrentEngine){
- $.jStore.CurrentEngine = this;
- }
- $.jStore.delegate.trigger('jStore-ready', this);
- }
- else $.jStore.delegate.trigger('jStore-failure', this);
- }).include();
- }
- }
- }
-
- // Set the current storage engine
- $.jStore.setCurrentEngine = function(name){
- if (!$.jStore.Instances.length ) // If no instances exist, attempt to load one
- return $.jStore.FindEngine();
-
- if (!name && $.jStore.Instances.length >= 1) { // If no name is specified, use the first engine
- $.jStore.delegate.trigger('jStore-ready', $.jStore.Instances[0]);
- return $.jStore.CurrentEngine = $.jStore.Instances[0];
- }
-
- if (name && $.jStore.Instances[name]) { // If a name is specified and exists, use it
- $.jStore.delegate.trigger('jStore-ready', $.jStore.Instances[name]);
- return $.jStore.CurrentEngine = $.jStore.Instances[name];
- }
-
- throw 'JSTORE_JRI_NO_MATCH';
- }
-
- // Test all possible engines for straightforward useability
- $.jStore.FindEngine = function(){
- $.each($.jStore.EngineOrder, function(k){
- if ($.jStore.Availability[this]()){ // Find the first, easiest option and use it.
- $.jStore.use(this, $.jStore.defaults.project, 'default');
- return false;
- }
- })
- }
-
- // Provide a simple interface for storing/getting values
- $.jStore.store = function(key, value){
- if (!$.jStore.CurrentEngine) return false;
-
- if ( !value ) // Executing a get command
- return $.jStore.CurrentEngine.get(key);
- // Executing a set command
- return $.jStore.CurrentEngine.set(key, value);
- }
- // Provide a simple interface for storing/getting values
- $.jStore.remove = function(key){
- if (!$.jStore.CurrentEngine) return false;
-
- return $.jStore.CurrentEngine.rem(key);
- }
-
- // Provide a chainable interface for storing values/getting a value at the end of a chain
- $.fn.store = function(key, value){
- if (!$.jStore.CurrentEngine) return this;
-
- var result = $.jStore.store(key, value);
-
- return !value ? result : this;
- }
-
- // Provide a chainable interface for removing values
- $.fn.removeStore = function(key){
- $.jStore.remove(key);
-
- return this;
- }
-
- // Provide a way for users to call for auto-loading
- $.jStore.load = function(){
- if ($.jStore.defaults.engine)
- return $.jStore.use($.jStore.defaults.engine, $.jStore.defaults.project, 'default');
-
- // Attempt to find a valid engine, and catch any exceptions if we can't
- try {
- $.jStore.FindEngine();
- } catch (e) {}
- }
-
-})(jQuery);
-/**
- * jStore Engine Core
- * Copyright (c) 2009 Eric Garside (http://eric.garside.name)
- */
-(function($){
-
- this.StorageEngine = Class.extend({
- init: function(project, name){
- // Configure the project name
- this.project = project;
- // The JRI name given by the manager
- this.jri = name;
- // Cache the data so we can work synchronously
- this.data = {};
- // The maximum limit of the storage engine
- this.limit = -1;
- // Third party script includes
- this.includes = [];
- // Create an event delegate for users to subscribe to event triggers
- this.delegate = new jStoreDelegate(this)
- .bind('engine-ready', function(){
- this.isReady = true;
- })
- .bind('engine-included', function(){
- this.hasIncluded = true;
- });
- // If enabled, the manager will check availability, then run include(), then check again
- this.autoload = false; // This should be changed by the engines, if they have required includes
- // When set, we're ready to transact data
- this.isReady = false;
- // When the includer is finished, it will set this to true
- this.hasIncluded = false;
- },
- // Performs all necessary script includes
- include: function(){
- var self = this,
- total = this.includes.length,
- count = 0;
-
- $.each(this.includes, function(){
- $.ajax({type: 'get', url: this, dataType: 'script', cache: true,
- success: function(){
- count++;
- if (count == total) self.delegate.trigger('engine-included');
- }
- })
- });
- },
- // This should be overloaded with an actual functionality presence check
- isAvailable: function(){
- return false;
- },
- /** Event Subscription Shortcuts **/
- ready: function(callback){
- if (this.isReady) callback.apply(this);
- else this.delegate.bind('engine-ready', callback);
- return this;
- },
- included: function(callback){
- if (this.hasIncluded) callback.apply(this);
- else this.delegate.bind('engine-included', callback);
- return this;
- },
- /** Cache Data Access **/
- get: function(key){
- return this.data[key] || null;
- },
- set: function(key, value){
- this.data[key] = value;
- return value;
- },
- rem: function(key){
- var beforeDelete = this.data[key];
- this.data[key] = null;
- return beforeDelete;
- }
- });
-
-})(jQuery);
-/*
- * jStore DOM Storage Engine
- * Copyright (c) 2009 Eric Garside (http://eric.garside.name)
- */
-(function($){
-
- // Set up a static test function for this instance
- var sessionAvailability = $.jStore.Availability.session = function(){
- return !!window.sessionStorage;
- },
- localAvailability = $.jStore.Availability.local = function(){
- return !!(window.localStorage || window.globalStorage);
- };
-
- this.jStoreDom = StorageEngine.extend({
- init: function(project, name){
- // Call the parental init object
- this._super(project, name);
-
- // The type of storage engine
- this.type = 'DOM';
-
- // Set the Database limit
- this.limit = 5 * 1024 * 1024;
- },
- connect: function(){
- // Fire our delegate to indicate we're ready for data transactions
- this.delegate.trigger('engine-ready');
- },
- get: function(key){
- var out = this.db.getItem(key);
- // Gecko's getItem returns {value: 'the value'}, WebKit returns 'the value'
- return out && out.value ? out.value : out
- },
- set: function(key, value){
- this.db.setItem(key,value);
- return value;
- },
- rem: function(key){
- var out = this.get(key);
- this.db.removeItem(key);
- return out
- }
- })
-
- this.jStoreLocal = jStoreDom.extend({
- connect: function(){
- // Gecko uses a non-standard globalStorage[ www.example.com ] DOM access object for persistant storage.
- this.db = !window.globalStorage ? window.localStorage : window.globalStorage[location.hostname];
- this._super();
- },
- isAvailable: localAvailability
- })
-
- this.jStoreSession = jStoreDom.extend({
- connect: function(){
- this.db = sessionStorage;
- this._super();
- },
- isAvailable: sessionAvailability
- })
-
- $.jStore.Engines.local = jStoreLocal;
- $.jStore.Engines.session = jStoreSession;
-
- // Store the ordering preference
- $.jStore.EngineOrder[ 1 ] = 'local';
-
-})(jQuery);
-/*
- * jStore Flash Storage Engine
- * Copyright (c) 2009 Eric Garside (http://eric.garside.name)
- * jStore.swf Copyright (c) 2008 Daniel Bulli (http://www.nuff-respec.com)
- */
-(function($){
-
- // Set up a static test function for this instance
- var avilability = $.jStore.Availability.flash = function(){
- return !!($.jStore.hasFlash('8.0.0'));
- }
-
- this.jStoreFlash = StorageEngine.extend({
- init: function(project, name){
- // Call the parental init object
- this._super(project, name);
-
- // The type of storage engine
- this.type = 'Flash';
-
- // Bind our flashReady function to the jStore Delegate
- var self = this;
- $.jStore.flashReady(function(){ self.flashReady() });
- },
- connect: function(){
- var name = 'jstore-flash-embed-' + this.project;
-
- // To make Flash Storage work on IE, we have to load up an iFrame
- // which contains an HTML page that embeds the object using an
- // object tag wrapping an embed tag. Of course, this is unnecessary for
- // all browsers except for IE, which, to my knowledge, is the only browser
- // in existance where you need to complicate your code to fix bugs. Goddamnit. :(
- $(document.body)
- .append('<iframe style="height:1px;width:1px;position:absolute;left:0;top:0;margin-left:-100px;" ' +
- 'id="jStoreFlashFrame" src="' +$.jStore.defaults.flash + '"></iframe>');
- },
- flashReady: function(e){
- var iFrame = $('#jStoreFlashFrame')[0];
-
- // IE
- if (iFrame.Document && $.isFunction(iFrame.Document['jStoreFlash'].f_get_cookie)) this.db = iFrame.Document['jStoreFlash'];
- // Safari && Firefox
- else if (iFrame.contentWindow && iFrame.contentWindow.document){
- var doc = iFrame.contentWindow.document;
- // Safari
- if ($.isFunction($('object', $(doc))[0].f_get_cookie)) this.db = $('object', $(doc))[0];
- // Firefox
- else if ($.isFunction($('embed', $(doc))[0].f_get_cookie)) this.db = $('embed', $(doc))[0];
- }
-
- // We're ready to process data
- if (this.db) this.delegate.trigger('engine-ready');
- },
- isAvailable: avilability,
- get: function(key){
- var out = this.db.f_get_cookie(key);
- return out == 'null' ? null : out;
- },
- set: function(key, value){
- this.db.f_set_cookie(key, value);
- return value;
- },
- rem: function(key){
- var beforeDelete = this.get(key);
- this.db.f_delete_cookie(key);
- return beforeDelete;
- }
- })
-
- $.jStore.Engines.flash = jStoreFlash;
-
- // Store the ordering preference
- $.jStore.EngineOrder[ 2 ] = 'flash';
-
- /**
- * Flash Detection functions copied from the jQuery Flash Plugin
- * Copyright (c) 2006 Luke Lutman (http://jquery.lukelutman.com/plugins/flash)
- * Dual licensed under the MIT and GPL licenses.
- * http://www.opensource.org/licenses/mit-license.php
- * http://www.opensource.org/licenses/gpl-license.php
- */
- $.jStore.hasFlash = function(version){
- var pv = $.jStore.flashVersion().match(/\d+/g),
- rv = version.match(/\d+/g);
-
- for(var i = 0; i < 3; i++) {
- pv[i] = parseInt(pv[i] || 0);
- rv[i] = parseInt(rv[i] || 0);
- // player is less than required
- if(pv[i] < rv[i]) return false;
- // player is greater than required
- if(pv[i] > rv[i]) return true;
- }
- // major version, minor version and revision match exactly
- return true;
- }
-
- $.jStore.flashVersion = function(){
- // ie
- try {
- try {
- // avoid fp6 minor version lookup issues
- // see: http://blog.deconcept.com/2006/01/11/getvariable-setvariable-crash-internetā¦
- var axo = new ActiveXObject('ShockwaveFlash.ShockwaveFlash.6');
- try { axo.AllowScriptAccess = 'always'; }
- catch(e) { return '6,0,0'; }
- } catch(e) {}
- return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version').replace(/\D+/g, ',').match(/^,?(.+),?$/)[1];
- // other browsers
- } catch(e) {
- try {
- if(navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin){
- return (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]).description.replace(/\D+/g, ",").match(/^,?(.+),?$/)[1];
- }
- } catch(e) {}
- }
- return '0,0,0';
- }
-
-})(jQuery);
-
-// Callback fired when ExternalInterface is established
-function flash_ready(){
- $.jStore.delegate.trigger('flash-ready');
-}
-/*
- * jStore Google Gears Storage Engine
- * Copyright (c) 2009 Eric Garside (http://eric.garside.name)
- */
-(function($){
-
- // Set up a static test function for this instance
- var avilability = $.jStore.Availability.gears = function(){
- return !!(window.google && window.google.gears)
- }
-
- this.jStoreGears = StorageEngine.extend({
- init: function(project, name){
- // Call the parental init object
- this._super(project, name);
-
- // The type of storage engine
- this.type = 'Google Gears';
-
- // Add required third-party scripts
- this.includes.push('http://code.google.com/apis/gears/gears_init.js');
-
- // Allow Autoloading on fail
- this.autoload = true;
- },
- connect: function(){
- // Create our database connection
- var db = this.db = google.gears.factory.create('beta.database');
- db.open( 'jstore-' + this.project );
- db.execute( 'CREATE TABLE IF NOT EXISTS jstore (k TEXT UNIQUE NOT NULL PRIMARY KEY, v TEXT NOT NULL)' );
-
- // Cache the data from the table
- this.updateCache();
- },
- updateCache: function(){
- // Read the database into our cache object
- var result = this.db.execute( 'SELECT k,v FROM jstore' );
- while (result.isValidRow()){
- this.data[result.field(0)] = result.field(1);
- result.next();
- } result.close();
-
- // Fire our delegate to indicate we're ready for data transactions
- this.delegate.trigger('engine-ready');
- },
- isAvailable: avilability,
- set: function(key, value){
- // Update the database
- var db = this.db;
- db.execute( 'BEGIN' );
- db.execute( 'INSERT OR REPLACE INTO jstore(k, v) VALUES (?, ?)', [key,value] );
- db.execute( 'COMMIT' );
- return this._super(key, value);
- },
- rem: function(key){
- // Update the database
- var db = this.db;
- db.execute( 'BEGIN' );
- db.execute( 'DELETE FROM jstore WHERE k = ?', [key] );
- db.execute( 'COMMIT' );
- return this._super(key);
- }
- })
-
- $.jStore.Engines.gears = jStoreGears;
-
- // Store the ordering preference
- $.jStore.EngineOrder[ 3 ] = 'gears';
-
-})(jQuery);
-/*
- * jStore HTML5 Specification Storage Engine
- * Copyright (c) 2009 Eric Garside (http://eric.garside.name)
- */
-(function($){
-
- // Set up a static test function for this instance
- var avilability = $.jStore.Availability.html5 = function(){
- return !!window.openDatabase
- }
-
- this.jStoreHtml5 = StorageEngine.extend({
- init: function(project, name){
- // Call the parental init object
- this._super(project, name);
-
- // The type of storage engine
- this.type = 'HTML5';
-
- // Set the Database limit
- this.limit = 1024 * 200;
- },
- connect: function(){
- // Create our database connection
- var db = this.db = openDatabase('jstore-' + this.project, '1.0', this.project, this.limit);
- if (!db) throw 'JSTORE_ENGINE_HTML5_NODB';
- db.transaction(function(db){
- db.executeSql( 'CREATE TABLE IF NOT EXISTS jstore (k TEXT UNIQUE NOT NULL PRIMARY KEY, v TEXT NOT NULL)' );
- });
-
- // Cache the data from the table
- this.updateCache();
- },
- updateCache: function(){
- var self = this;
- // Read the database into our cache object
- this.db.transaction(function(db){
- db.executeSql( 'SELECT k,v FROM jstore', [], function(db, result){
- var rows = result.rows, i = 0, row;
- for (; i < rows.length; ++i){
- row = rows.item(i);
- self.data[row.k] = row.v;
- }
-
- // Fire our delegate to indicate we're ready for data transactions
- self.delegate.trigger('engine-ready');
- });
- });
- },
- isAvailable: avilability,
- set: function(key, value){
- // Update the database
- this.db.transaction(function(db){
- db.executeSql( 'INSERT OR REPLACE INTO jstore(k, v) VALUES (?, ?)', [key,value]);
- });
- return this._super(key, value);
- },
- rem: function(key){
- // Update the database
- this.db.transaction(function(db){
- db.executeSql( 'DELETE FROM jstore WHERE k = ?', [key] )
- })
- return this._super(key);
- }
- })
-
- $.jStore.Engines.html5 = jStoreHtml5;
-
- // Store the ordering preference
- $.jStore.EngineOrder[ 0 ] = 'html5';
-
-})(jQuery);
-/*
- * jStore IE Storage Engine
- * Copyright (c) 2009 Eric Garside (http://eric.garside.name)
- */
-(function($){
-
- // Set up a static test function for this instance
- var avilability = $.jStore.Availability.ie = function(){
- return !!window.ActiveXObject;
- }
-
- this.jStoreIE = StorageEngine.extend({
- init: function(project, name){
- // Call the parental init object
- this._super(project, name);
-
- // The type of storage engine
- this.type = 'IE';
-
- // Allow Autoloading on fail
- this.limit = 64 * 1024;
- },
- connect: function(){
- // Create a hidden div to store attributes in
- this.db = $('<div style="display:none;behavior:url(\'#default#userData\')" id="jstore-' + this.project + '"></div>')
- .appendTo(document.body).get(0);
- // Fire our delegate to indicate we're ready for data transactions
- this.delegate.trigger('engine-ready');
- },
- isAvailable: avilability,
- get: function(key){
- this.db.load(this.project);
- return this.db.getAttribute(key);
- },
- set: function(key, value){
- this.db.setAttribute(key, value);
- this.db.save(this.project);
- return value;
- },
- rem: function(key){
- var beforeDelete = this.get(key);
- this.db.removeAttribute(key);
- this.db.save(this.project);
- return beforeDelete;
- }
- })
-
- $.jStore.Engines.ie = jStoreIE;
-
- // Store the ordering preference
- $.jStore.EngineOrder[ 4 ] = 'ie';
-
-})(jQuery);
diff -r a9402fc7ce1b -r 5536d57237a3 static/scripts/jquery.jstore.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/static/scripts/jquery.jstore.js Fri Apr 09 18:09:54 2010 -0400
@@ -0,0 +1,1810 @@
+/*!
+ * jStore 2.0 - Persistent Client Side Storage
+ *
+ * Copyright (c) 2010 Eric Garside (http://eric.garside.name/)
+ * Dual licensed under:
+ * MIT: http://www.opensource.org/licenses/mit-license.php
+ * GPLv3: http://www.opensource.org/licenses/gpl-3.0.html
+ *
+ * ---------------------------
+ *
+ * jStore Flash Storage Component
+ *
+ * Copyright (c) 2006 Jeff Lerman (jeff(a)blip.tv)
+ * Licensed under the Creative Commons Attribution 3.0 United States License:
+ * http://creativecommons.org/licenses/by/3.0/us
+ */
+
+"use strict";
+
+/*global Class, window, jQuery, ActiveXObject, google */
+
+/*jslint white: true, browser: true, onevar: true, undef: true, eqeqeq: true, bitwise: true, regexp: false, strict: true, newcap: true, immed: true, maxerr: 50, indent: 4 */
+
+(function ($, window) {
+
+ //------------------------------
+ //
+ // Constants
+ //
+ //------------------------------
+
+ //------------------------------
+ // Exceptions
+ //------------------------------
+
+ /**
+ * An exception thrown by the StorageEngine class whenever its data accessor methods
+ * are called before the engine is ready to transact data.
+ */
+ var EX_UNSTABLE = 'JSTORE_ENGINE_UNSTABLE',
+
+ /**
+ * An exception thrown by jStore whenever an undefined storage engine is referenced for
+ * some task by an invalid JRI (jStore Resource Identifier).
+ */
+ EX_UNKNOWN = 'JSTORE_UNKNOWN_ENGINE_REQUESTED',
+
+ /**
+ * An exception thrown by jStore whenever a given flavor of storage is double defined.
+ */
+ EX_COLLISION = 'JSTORE_ENGINE_NAMESPACE_COLLISION',
+
+ /**
+ * An exception thrown by jStore whenever a jri is double applied to a resource.
+ */
+ EX_DUPLICATE = 'JSTORE_RESOURCE_NAMESPACE_COLLISION',
+
+ /**
+ * An exception thrown by jStore whenever a given flavor of storage has no defined engine.
+ */
+ EX_UNAVAILABLE = 'JSTORE_ENGINE_UNAVAILABLE',
+
+ /**
+ * An exception thrown by jStore whenever an invalid flavor type is used.
+ */
+ EX_INVALID = 'JSTORE_INVALID_FLAVOR',
+
+ //------------------------------
+ // Regular Expressions
+ //------------------------------
+
+ /**
+ * Regular expression to test property values for being JSON.
+ */
+ RX_JSON = (function ()
+ {
+ try
+ {
+ return new RegExp('^("(\\\\.|[^"\\\\\\n\\r])*?"|[,:{}\\[\\]0-9.\\-+Eaeflnr-u \\n\\r\\t])+?$');
+ }
+ catch (e)
+ {
+ return (/^(true|false|null|\[.*\]|\{.*\}|".*"|\d+|\d+\.\d+)$/);
+ }
+ }()),
+
+ //------------------------------
+ // Storage Flavors
+ //------------------------------
+
+ /**
+ * The storage flavor identifier for HTML5 local storage.
+ */
+ FLAVOR_LOCAL = 'jstore-html5-local',
+
+ /**
+ * The storage flavor identifier for HTML5 database storage.
+ */
+ FLAVOR_SQL = 'jstore-html5-sql',
+
+ /**
+ * The storage flavor identifier for Adobe Flash SharedObject storage.
+ */
+ FLAVOR_FLASH = 'jstore-flash',
+
+ /**
+ * The storage flavor identifier for Google Gears storage.
+ */
+ FLAVOR_GEARS = 'jstore-google-gears',
+
+ /**
+ * The storage flavor identifier for Internet Explorer storage, available to IE7 and IE6.
+ */
+ FLAVOR_MSIE = 'jstore-msie',
+
+ //------------------------------
+ //
+ // Property Declaration
+ //
+ //------------------------------
+
+ /**
+ * The base StorageEngine class which each "storage flavor" will extend to meet the
+ * requirements for its specific implementation.
+ */
+ StorageEngine,
+
+ /**
+ * The jStore object. Internal to this closure, jStore is referenced by "_". It is
+ * exposed to jQuery below, and made publicly accessible through jQuery.jStore
+ */
+ _ = {},
+
+ /**
+ * The engines available to jStore for use. These are the class definitions for flavored
+ * storage engines.
+ *
+ * Signature:
+ * {
+ * <storageFlavor>: <flavoredStorageEngineDefinition>,
+ *
+ * ...
+ * }
+ */
+ definitions = {},
+
+ /**
+ * Active engines instantiated by jStore, indexed by their JRI.
+ *
+ * Signature:
+ * {
+ * <engineJRI>: <engineInstance>,
+ *
+ * ...
+ * }
+ */
+ engines = {},
+
+ /**
+ * If we are going to be using the flash storage engine, we want to postpone the jStore ready event until the jStore
+ * isFlashReady flag is also true. This property is set whenever flash is determined to be the storage engine.
+ */
+ waitForFlash = false,
+
+ /**
+ * Storage for listeners, indexed by content and event type.
+ *
+ * Signature:
+ * {
+ * <context>:
+ * {
+ * <eventType>: [<listener>, ...],
+ *
+ * ...
+ * },
+ *
+ * ...
+ * }
+ */
+ events = {},
+
+ /**
+ * The configuration for this implementation.
+ *
+ * Signature:
+ * {
+ * project: <defaultProjectName>,
+ *
+ * flash: <pathToFlashBootloader>,
+ *
+ * json: <pathToJSONFile>,
+ *
+ * errorCallback: <listenerToNotifyOnError>
+ * }
+ */
+ configurations =
+ {
+ project: undefined,
+
+ flash: 'jStore.Flash.html',
+
+ json: 'browser.json.js'
+ },
+
+ /**
+ * The active storage engine, being used to satisfy the get/set/remove functions on the jStore and jQuery
+ * objects.
+ */
+ active;
+
+ //------------------------------
+ //
+ // Internal Methods
+ //
+ //------------------------------
+
+ /**
+ * Determine if the given flavor is valid.
+ *
+ * @param flavor The flavor to test.
+ *
+ * @return True if the flavor is valid, false otherwise.
+ */
+ function validFlavor(flavor)
+ {
+ switch (flavor)
+ {
+
+ case FLAVOR_LOCAL:
+ case FLAVOR_SQL:
+ case FLAVOR_FLASH:
+ case FLAVOR_GEARS:
+ case FLAVOR_MSIE:
+ return true;
+
+ default:
+ return false;
+
+ }
+ }
+
+ /**
+ * Performs enhanced type comparison on an object. This is more reliable method
+ * of type checking a variable than a simple typeof comparison. The reason is that,
+ * typeof will reduce to the lowest common type.
+ *
+ * "typeof []" returns Object, and not Array.
+ * "typeof {}" returns Object as well.
+ *
+ * typecheck( [], 'Array' ) : returns true;
+ * typecheck( [], 'Object' ) : returns false;
+ *
+ * @param type The variable type to check.
+ *
+ * @param compare A string representing the literal type to check.
+ *
+ * @return True if the variable "type" matches the compare literal.
+ */
+ function typecheck(type, compare)
+ {
+ return !type ? false : type.constructor.toString().match(new RegExp(compare + '\\(\\)', 'i')) !== null;
+ }
+
+ /**
+ * If the provided listener is a valid function, it will be triggered with the provided context
+ * and parameters.
+ *
+ * @param listener The listener being triggered.
+ *
+ * @param context The context to provide to the listener.
+ *
+ * @param parameters The parameters to pass to the listener as arguments.
+ *
+ * @return The response of the notified listener.
+ */
+ function notify(listener, context, parameters)
+ {
+ if (typecheck(listener, 'Function'))
+ {
+ return listener.apply(context || _, typecheck(parameters, 'Array') ? parameters : [parameters]);
+ }
+ }
+
+ /**
+ * Load the given script.
+ *
+ * @param path The path to the file to include.
+ *
+ * @param listener The listener to notify when the file finishes loading.
+ */
+ function loadScript(path, listener)
+ {
+ $.ajax(
+ {
+ url: path,
+ complete: listener || $.noop(),
+ type: 'GET',
+ dataType: 'script',
+ cache: false
+ });
+ }
+
+ /**
+ * Checks the type of the value, and returns a value safe to persist in any client-side mechanism.
+ *
+ * @param value The value which should be prepared for storage.
+ *
+ * @return A value safe for storage.
+ */
+ function prepareForStorage(value)
+ {
+ if (value === undefined)
+ {
+ return '';
+ }
+
+ if (typecheck(value, 'Object') ||
+ typecheck(value, 'Array') ||
+ typecheck(value, 'Function'))
+ {
+ return JSON.stringify(value);
+ }
+
+ return value;
+ }
+
+ /**
+ * Checks the type of the value, and returns a value safe for access in any client-side mechanism.
+ *
+ * @param value The value which should be prepared for use.
+ *
+ * @return A value safe for use.
+ */
+ function prepareForRevival(value)
+ {
+ return RX_JSON.test(value) ? JSON.parse(value) : value;
+ }
+
+ /**
+ * Normalize a key before using it, to ensure it's valid.
+ *
+ * @param key The key to normalize.
+ *
+ * @return A normalized key, safe for storage.
+ */
+ function normalizeKey(key)
+ {
+ return key.replace(/^\s+|\s+$/g, "");
+ }
+
+ /**
+ * Define a flavored storage engine.
+ *
+ * @throws EX_COLLISION, EX_INVALID
+ *
+ * @param flavor The flavor of engine being defined.
+ *
+ * @param definition An object containing the new properties and methods for the engine extension.
+ *
+ * @param availability A function to invoke which must return a boolean value indicating the
+ * availability of the storage flavor on this browser.
+ */
+ function define(flavor, definition, availability)
+ {
+ if (!validFlavor(flavor))
+ {
+ throw EX_INVALID;
+ }
+
+ if (availability[flavor] !== undefined)
+ {
+ throw EX_COLLISION;
+ }
+
+ /**
+ * The logic here has been reworked so unavailable flavors are discarded, so we don't needlessly
+ * bloat the runtime size of jStore.
+ */
+ if (notify(availability) === true)
+ {
+ _.available[flavor] = true;
+
+ definition.flavor = flavor;
+
+ definitions[flavor] = StorageEngine.extend(definition);
+ }
+ else
+ {
+ _.available[flavor] = false;
+
+ // Filter the invalid flavor out of the priority list.
+ _.enginePriority = $.map(_.enginePriority, function (engine)
+ {
+ if (engine === flavor)
+ {
+ return null;
+ }
+ else
+ {
+ return engine;
+ }
+ });
+ }
+ }
+
+ /**
+ * Make the jStore library ready.
+ */
+ function makeReady()
+ {
+ if (_.isReady)
+ {
+ return;
+ }
+
+ if ((waitForFlash && _.isFlashReady) || !waitForFlash)
+ {
+ _.isReady = true;
+ _.trigger('jstore-ready', [engines[active]]);
+ }
+ }
+
+ /**
+ * Create a best-fit engine.
+ */
+ function createBestFitEngine()
+ {
+ _.create(_.enginePriority[0], undefined, 'best-fit');
+ }
+
+ /**
+ * Get the flash version currently supported in this browser.
+ *
+ * @return The flash version.
+ */
+ function flashVersion()
+ {
+ // MSIE
+ try
+ {
+ // avoid fp6 minor version lookup issues
+ // see: http://blog.deconcept.com/2006/01/11/getvariable-setvariable-crash-internetā¦
+ var axo = new ActiveXObject('ShockwaveFlash.ShockwaveFlash.6');
+
+ try
+ {
+ axo.AllowScriptAccess = 'always';
+ }
+ catch (axo_e)
+ {
+ return '6,0,0';
+ }
+
+ return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version').replace(/\D+/g, ',').match(/^,?(.+),?$/)[1];
+ }
+
+ // Real browsers
+ catch (e)
+ {
+ try
+ {
+ if (navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin)
+ {
+ return (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]).description.replace(/\D+/g, ",").match(/^,?(.+),?$/)[1];
+ }
+ }
+ catch (flash_e)
+ {}
+ }
+
+ return '0,0,0';
+ }
+
+ /**
+ * Flash Detection functions copied from the jQuery Flash Plugin
+ *
+ * Copyright (c) 2006 Luke Lutman (http://jquery.lukelutman.com/plugins/flash)
+ *
+ * Dual licensed under the MIT and GPL licenses.
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.opensource.org/licenses/gpl-license.php
+ *
+ * @param version The version to compare to.
+ *
+ * @return True if the version is greater than or equal to the required version, false otherwise.
+ */
+ function hasFlashVersion(version)
+ {
+ var playerVersion = flashVersion().match(/\d+/g),
+ requiredVersion = version.match(/\d+/g),
+ index = 0,
+ player,
+ required;
+
+ for (; index < 3; index++)
+ {
+ player = parseInt(playerVersion[index], 10);
+ required = parseInt(requiredVersion[index], 10);
+
+ // Player version is less than what is required.
+ if (player < required)
+ {
+ return false;
+ }
+
+ // Player version is greater than what is required.
+ else if (player > required)
+ {
+ return true;
+ }
+ }
+
+ // Player and required version match exactly.
+ return true;
+ }
+
+ //------------------------------
+ //
+ // Plugin Definition
+ //
+ //------------------------------
+
+ //------------------------------
+ // Error Declaration
+ //------------------------------
+
+ //------------------------------
+ // Plugin Creation
+ //------------------------------
+
+ /**
+ * The jStore object. Manages a collection of StorageEngines for particular "storage flavors", or the types
+ * of storage solutions available to each browser.
+ *
+ * 2.0 Version Notes:
+ *
+ * - The user is now responsible for third-party script includes, with the exception of flash.
+ *
+ * - jStore has been given sole responsibility for testing engine availability.
+ *
+ * - For the sake of naming conventions, all property names now start with a lowercase, and are camel-cased.
+ *
+ * The following properties have been changed since the 1.2.x release:
+ *
+ * - EngineOrder: For the sake of naming conventions, renamed to enginePriority.
+ *
+ * The following properties and methods have been removed since the 1.2.x release:
+ *
+ * - Availability: jStore's engines would add their availability tests to this object, so jStore could test
+ * them. With the changes to how availability testing works, this property has been removed.
+ * A new property, "available" on jStore contains a set of available engines.
+ *
+ * - Engines: Formerly contained the definitions of storage engines. This property has been removed, and
+ * storage of these definitions has been moved internal to the closure.
+ *
+ * - Instances: Formerly contained instantiated storage engines. This property has been removed, and storage
+ * of instantiated engines has been moved internal to the closure.
+ *
+ * - CurrentEngine: Formerly contained the active storage engine being used for transacting data through the jStore
+ * and/or jQuery objects. This property has been removed, and storage of the current engine has
+ * been moved internal to the closure. A new method, "activeEngine" has been added to jQuery to
+ * get and set the active engine to use.
+ *
+ * - defaults: Formerly used to set the implementation options for jStore. This property has been removed and
+ * replaced with a new configuration metho on the jStore object.
+ *
+ * - delegate: The delegate class has been removed in favor of a much simpler bind/trigger accessor system, which
+ * is accessible contextually through storage engines, or generically through jStore.
+ *
+ * + fail: This registration class bound events on the delegate for jstore-fail events. Instead, use:
+ * jStore.bind('jstore-failure', listener);
+ *
+ * + flashReady: This registration class bound events on the delegate for flash-ready events. The jstore-ready method
+ * now accounts for waiting for flash readyness, if and only if the flash engine is being used. Simply
+ * call to jStore.ready().
+ *
+ * + load: Replaced with the init() method, which performs the same basic functions as the old load() method. Also,
+ * the init function is now domready safe, meaning it wraps itself in a domready listener, so the end user
+ * doesn't have to.
+ *
+ * + FindEngine: Removed entirely. The functionality provided by this method now implicitly occurs with the new define()
+ * system implemented for engine flavors.
+ *
+ * + setCurrentEngine: Replaced by activeEngine(). Set the current active engine by passing in the JRI.
+ *
+ * + safeStore: Replaced by a method internal to this closure, "prepareForStorage".
+ *
+ * + safeResurrect: Replaced by a method internal to this closure, "prepareForRevival".
+ *
+ * + use: Replaced by "create".
+ */
+ $.extend(_, {
+
+ //------------------------------
+ // Properties
+ //------------------------------
+
+ /**
+ * The priority order in which engines should be tested for use. The lower their index in the array, the higher
+ * their priority for use.
+ *
+ * Be weary when reconfiguring the priority order of engines! jStore will use the first available engine it finds
+ * based on its priority when autoloading.
+ *
+ * This array is filtered out as engines are defined, with invalid engines being removed.
+ *
+ * Signature:
+ * [FLAVOR_<storageFlavor>, ...]
+ */
+ enginePriority: [FLAVOR_LOCAL, FLAVOR_SQL, FLAVOR_FLASH, FLAVOR_MSIE],
+
+ /**
+ * A collection of the availability states of engines, indexed by their flavor.
+ *
+ * Signature:
+ * {
+ * <storageFlavor>: true|false,
+ *
+ * ...
+ * }
+ */
+ available: {},
+
+ /**
+ * Flag to determine if the jStore library is ready. jStore becomes ready once the dom is ready and all necessary
+ * startup procedures required by jStore to function properly are completed.
+ */
+ isReady: false,
+
+ /**
+ * With the flash storage engine, we have to jump through a couple of hoops before the flash engine is ready to work.
+ * This flag tracks whether or not the flash storage is available.
+ */
+ isFlashReady: false,
+
+ /**
+ * The available engine flavors.
+ */
+ flavors:
+ {
+ local: FLAVOR_LOCAL,
+
+ sql: FLAVOR_SQL,
+
+ flash: FLAVOR_FLASH,
+
+ gears: FLAVOR_GEARS,
+
+ msie: FLAVOR_MSIE
+ },
+
+ //------------------------------
+ // Constructor
+ //------------------------------
+
+ /**
+ * Constructor.
+ *
+ * @throws EX_INVALID
+ *
+ * @param project The name of the jStore project. Used to generate a JRI for the engine we create.
+ *
+ * @param configuration Optionally, an object containing configuration options for this implementation.
+ *
+ * @param flavor Optionally, the flavor of storage to use. If not provided, jStore will pick the
+ * best flavor, based on the current browser.
+ *
+ * @return jStore
+ */
+ init: function (project, configuration, flavor)
+ {
+ // Extend our plugin configurations
+ $.extend(configurations, {project: project}, configuration);
+
+ $(function ()
+ {
+ // If JSON parsing isn't defined in this browser, include it.
+ if (window.JSON === undefined)
+ {
+ loadScript(configurations.json);
+ }
+
+ // If we have an explicit flavor to use, use it.
+ if (flavor !== undefined)
+ {
+ _.create(flavor, project, 'default');
+ }
+
+ // Otherwise, attempt to create a best-fit engine.
+ else
+ {
+ createBestFitEngine();
+ }
+ });
+
+ return _;
+ },
+
+ //------------------------------
+ // Methods
+ //------------------------------
+
+ /**
+ * Create an instance of a flavored engine.
+ *
+ * @throws EX_INVALID, EX_UNAVAILABLE, EX_DUPLICATE
+ *
+ * @param flavor The flavor to create the engine with.
+ *
+ * @param project The project identifier for this instance.
+ *
+ * @param identifier Some arbitrary identifier for this project instance of the engine.
+ *
+ * @return The created instance.
+ */
+ create: function (flavor, project, identifier)
+ {
+ project = project || configurations.project || location.hostname.replace(/\./g, '-') || 'unknown';
+
+ if (!validFlavor(flavor))
+ {
+ throw EX_INVALID;
+ }
+
+ if (definitions[flavor] === undefined)
+ {
+ throw EX_UNAVAILABLE;
+ }
+
+ var jri = (identifier !== undefined ? identifier + '.' : '') + project + '.' + flavor,
+ engine;
+
+ if (engines[jri] !== undefined)
+ {
+ throw EX_DUPLICATE;
+ }
+
+ // Create our engine instance.
+ engine = engines[jri] = new definitions[flavor](project, jri);
+
+ // Set up a listener for our jstore-engine-ready event.
+ engine.ready(function ()
+ {
+ _.trigger('jstore-engine-ready', [engine]);
+ });
+
+ if (flavor === FLAVOR_FLASH && !_.isFlashReady)
+ {
+ if (active === undefined)
+ {
+ waitForFlash = true;
+ }
+
+ // Define a window-accessible function for flash to call via ExternalInterface
+ window.jstore_ready = function ()
+ {
+ _.isFlashReady = true;
+ _.trigger('flash-ready');
+
+ if (active === undefined)
+ {
+ makeReady();
+ }
+
+ // Remove the callback from the window scope, as it is no longer necessary
+ window.flash_ready = undefined;
+ };
+
+ window.jstore_error = function (message)
+ {
+ _.trigger('jstore-error', ['JSTORE_FLASH_EXCEPTION', null, message]);
+ };
+
+ $('<iframe style="height:1px;width:1px;position:absolute;left:0;top:0;margin-left:-100px;" id="jStoreFlashFrame" src="' +
+ configurations.flash + '"></iframe>').appendTo('body');
+ }
+ else if (active === undefined)
+ {
+ active = jri;
+ makeReady();
+ }
+
+ return engine;
+ },
+
+ /**
+ * Fetch an engine by it's JRI.
+ *
+ * @param jri The JRI of the engine to retrieve.
+ *
+ * @return The requested engine.
+ */
+ engine: function (jri)
+ {
+ return engines[jri];
+ },
+
+ /**
+ * Returns the active storage engine being used. If a value is passed, sets that engine as the active engine.
+ *
+ * @throws EX_UNKNOWN
+ *
+ * @param jri Optionally, the JRI of the engine to make active, if it should be changed.
+ *
+ * @return The active storage engine.
+ */
+ activeEngine: function (jri)
+ {
+ if (jri !== undefined)
+ {
+ if (engines[jri] === undefined)
+ {
+ throw EX_UNKNOWN;
+ }
+ else
+ {
+ active = jri;
+ }
+ }
+
+ return engines[active];
+ },
+
+ /**
+ * Bind an event listener.
+ *
+ * @param event The event to bind a listener on.
+ *
+ * @param listener The listener to notify when the event occurs.
+ *
+ * @param context The context of the binding. A string representing the engine flavor
+ * binding the event, or undefined to indicate it's a jStore event.
+ *
+ * @return jStore
+ */
+ bind: function (event, listener, context)
+ {
+ context = context || 'jstore';
+
+ if (events[context] === undefined)
+ {
+ events[context] = {};
+ }
+
+ if (events[context][event] === undefined)
+ {
+ events[context][event] = [listener];
+ }
+ else
+ {
+ events[context][event].push(listener);
+ }
+
+ return _;
+ },
+
+ /**
+ * Trigger an event, notifying any bound listeners.
+ *
+ * @param event The event to trigger.
+ *
+ * @param parameters Any additional parameters to pass to the listeners being notified.
+ *
+ * @param context The context of the binding. A string representing the engine flavor
+ * binding the event, or undefined to indicate it's a jStore event.
+ *
+ * @return jStore
+ */
+ trigger: function (event, parameters, context)
+ {
+ context = context || 'jstore';
+
+ if (events[context] !== undefined)
+ {
+ if (events[context][event] !== undefined)
+ {
+ $.each(events[context][event], function ()
+ {
+ notify(this, _, parameters);
+ });
+ }
+ }
+
+ return _;
+ },
+
+ /**
+ * Bind a listener to be notified when jStore causes a non-fatal exception.
+ *
+ * @param listener The listener to notify when a failure occurs.
+ */
+ error: function (listener)
+ {
+ _.bind('jstore-error', listener);
+ },
+
+ /**
+ * Bind a listener to be notified when jStore is ready.
+ *
+ * @param listener The listener to notify when jStore is ready.
+ *
+ * @return jStore
+ */
+ ready: function (listener)
+ {
+ if (_.isReady)
+ {
+ notify(listener);
+ }
+ else
+ {
+ _.bind('jstore-ready', listener);
+ }
+
+ return _;
+ },
+
+ /**
+ * Bind a listener to be notified when jStore and the default engine are ready.
+ *
+ * @param listener The listener to notify when jStore and it's default engine are ready.
+ *
+ * @return jStore
+ */
+ engineReady: function (listener)
+ {
+ if (_.isReady)
+ {
+ notify(listener);
+ }
+ else
+ {
+ _.bind('jstore-engine-ready', listener);
+ }
+
+ return _;
+ },
+
+ /**
+ * A combined getter/setter for the active engine.
+ *
+ * @param key The key of the property to get, or set.
+ *
+ * @param value If a valid value is provided, sets the engine.
+ *
+ * @return The requested property value.
+ */
+ store: function (key, value)
+ {
+ return value === undefined ? _.get(key) : _.set(key, value);
+ },
+
+ /**
+ * Remove a property from the active engine.
+ *
+ * @param key The key of the property to remove.
+ *
+ * @return The value of the property before removal.
+ */
+ remove: function (key)
+ {
+ return _.activeEngine().remove(key);
+ },
+
+ /**
+ * Get a property from the active engine.
+ *
+ * @param key The key of the property to get.
+ *
+ * @return The value of the property.
+ */
+ get: function (key)
+ {
+ return _.activeEngine().get(key);
+ },
+
+ /**
+ * Set a property on the active engine.
+ *
+ * @param key The key of the property to set.
+ *
+ * @param value The value to set the property to.
+ *
+ * @return The new value of the property.
+ */
+ set: function (key, value)
+ {
+ return _.activeEngine().set(key, value);
+ }
+
+ });
+
+ //------------------------------
+ // Core Extension
+ //------------------------------
+
+ //------------------------------
+ //
+ // Class Definition
+ //
+ //------------------------------
+
+ /**
+ * The StorageEngine class is the unified API through which jStore accesses and manipulates
+ * the various storage flavors available.
+ *
+ * 2.0 Version Notes:
+ *
+ * - All third-party loading is now the responsibility of the developer.
+ *
+ * - The delegate class has been removed entirely. Engines have been given "bind" and "trigger" methods
+ * to interact directly with the delegate like-replacement that has been added to jStore.
+ *
+ * - Engine availability has been moved out of the engines themselves, and elevated to a jStore
+ * responsibility.
+ *
+ * The following methods have changed since the 1.2.x release:
+ *
+ * - get: When "get"ting a non-stored property, the get function will now return "undefined"
+ * instead of "null". "null" can now be used as a valid property value.
+ *
+ * - rem: Renamed to "remove". I always felt dirty about "rem" being vaguely explicit.
+ *
+ * The following properties have been removed since the 1.2.x release:
+ *
+ * - autoload: Part of the third-party loading logic.
+ *
+ * - hasIncluded: Part of the third-party loading logic.
+ *
+ * - includes: Part of the third-party loading logic.
+ *
+ * - isAvailable: Part of the availability logic elevated to jStore.
+ *
+ * @throws EX_UNSTABLE
+ */
+ StorageEngine = Class.extend({
+
+ //------------------------------
+ // Properties
+ //------------------------------
+
+ /**
+ * The project which owns this storage engine.
+ */
+ project: undefined,
+
+ /**
+ * The JRI (jStore Resource Identifier) acts as a uuid for this specific instance
+ * of the storage engine.
+ */
+ jri: undefined,
+
+ /**
+ * The flavor of this engine.
+ */
+ flavor: undefined,
+
+ /**
+ * The actual database object which data is transacted through.
+ */
+ database: undefined,
+
+ /**
+ * A StorageEngine should always respond to fetch requests synchronously. However, some
+ * of the storage flavors require callback-based asynchronous access. To get around this,
+ * we simlpy require all engines to function off a primary data cache, to allow for
+ * synchronous access across all implementations.
+ *
+ * Signature:
+ * {
+ * <propertyKey>: <propertyValue>,
+ *
+ * ...
+ * }
+ */
+ data: undefined,
+
+ /**
+ * A number of storage engines enforce a size limit as to what they will persist for a given site.
+ * This limit is not monitored or computed by jStore currently, and this property will merely give
+ * a static indication of the total size alloted to the engine, as defined by the storage flavor.
+ */
+ limit: undefined,
+
+ /**
+ * Each storage flavor has a different process to go through before it's "ready" to transact data. This
+ * property stores the state of the engine's readyness, and uses it to notify listeners whenever jStore
+ * is ready to function.
+ */
+ isReady: undefined,
+
+ //------------------------------
+ // Constructor
+ //------------------------------
+
+ /**
+ * Constructor.
+ *
+ * @param project The project which instantiated this engine.
+ *
+ * @param jri The uuid assigned to this instance by jStore.
+ */
+ init: function (project, jri)
+ {
+ this.project = project;
+ this.jri = jri;
+ this.data = {};
+ this.isReady = false;
+ this.updateCache();
+ },
+
+ //------------------------------
+ // Methods
+ //------------------------------
+
+ /**
+ * Update the cache.
+ */
+ updateCache: function ()
+ {
+ this.isReady = true;
+ this.trigger('engine-ready', [this]);
+ },
+
+ /**
+ * Bind a listener to an event dispatched by this engine.
+ *
+ * @param event The event to bind on.
+ *
+ * @param listener The listener to notify when the event occurs.
+ */
+ bind: function (event, listener)
+ {
+ _.bind(event, listener, this.jri);
+ },
+
+ /**
+ * Trigger an event, notifying all bound listeners.
+ *
+ * @param event The event to trigger.
+ *
+ * @param parameters An optional Array of parameters to pass to the listeners.
+ */
+ trigger: function (event, parameters)
+ {
+ _.trigger(event, parameters, this.jri);
+ },
+
+ /**
+ * Bind a listener to the StorageEngine's ready event.
+ *
+ * @param listener The listener to notify whenever this engine is ready to transact data.
+ */
+ ready: function (listener)
+ {
+ if (this.isReady)
+ {
+ notify(listener, this);
+ }
+ else
+ {
+ this.bind('engine-ready', listener);
+ }
+ },
+
+ /**
+ * Get a property from the StorageEngine.
+ *
+ * @param key The property key of the data to retrieve.
+ *
+ * @return The property value, or "undefined" if the property isn't stored.
+ */
+ get: function (key)
+ {
+ this.__interruptAccess();
+
+ return this.data[key];
+ },
+
+ /**
+ * Sets a property in the StorageEngine.
+ *
+ * @param key The key of the property.
+ *
+ * @param value The value of the property.
+ *
+ * @return The new value of the property.
+ */
+ set: function (key, value)
+ {
+ this.__interruptAccess();
+
+ key = normalizeKey(key);
+
+ try
+ {
+ this.__set(key, value);
+ }
+ catch (e)
+ {
+ _.trigger('jstore-error', ['JSTORE_STORAGE_FAILURE', this.jri, e]);
+ }
+
+ this.data[key] = value;
+
+ return value;
+ },
+
+ /**
+ * Removes a property from the StorageEngine.
+ *
+ * @param key The property key of the data to remove.
+ *
+ * @return The value of the property, before it was removed.
+ */
+ remove: function (key)
+ {
+ this.__interruptAccess();
+
+ key = normalizeKey(key);
+
+ try
+ {
+ this.__remove(key);
+ }
+ catch (e)
+ {
+ _.trigger('jstore-error', ['JSTORE_REMOVE_FAILURE', this.jri, e]);
+ }
+
+ var buffer = this.data[key];
+
+ this.data[key] = undefined;
+
+ return buffer;
+ },
+
+ //------------------------------
+ // Internal Methods
+ //------------------------------
+
+ /**
+ * Ensures the engine is in a stable state for transacting data.
+ *
+ * @throws EX_UNSTABLE
+ */
+ __interruptAccess: function ()
+ {
+ if (!this.isReady)
+ {
+ throw EX_UNSTABLE;
+ }
+ },
+
+ /**
+ * Sets a property in the StorageEngine. This method should be overloaded to provide actual
+ * storage flavor integration.
+ *
+ * @param key The key of the property.
+ *
+ * @param value The value of the property.
+ *
+ * @return The new value of the property.
+ */
+ __set: function (key, value)
+ {
+ return;
+ },
+
+ /**
+ * Removes a property from the StorageEngine. This method should be overloaded to provide actual
+ * storage flavor integration.
+ *
+ * @param key The property key of the data to remove.
+ *
+ * @return The value of the property, before it was removed.
+ */
+ __remove: function (key)
+ {
+ return;
+ }
+
+ });
+
+ //------------------------------
+ //
+ // jQuery Hooks
+ //
+ //------------------------------
+
+ $.extend($.fn, {
+
+ //------------------------------
+ // Methods
+ //------------------------------
+
+ /**
+ * A combined getter/setter for the active engine.
+ *
+ * @param key The key of the property to get, or set.
+ *
+ * @param value If a valid value is provided, sets the engine.
+ *
+ * @return jQuery
+ */
+ store: function (key, value)
+ {
+ if (value === undefined)
+ {
+ _.get(key);
+ }
+ else
+ {
+ _.set(key, value);
+ }
+
+ return this;
+ },
+
+ /**
+ * Remove a property from the active engine.
+ *
+ * @param key The key of the property to remove.
+ *
+ * @return jQuery
+ */
+ removeStore: function (key)
+ {
+ _.activeEngine().remove(key);
+
+ return this;
+ },
+
+ /**
+ * Get a property from the active engine.
+ *
+ * @param key The key of the property to get.
+ *
+ * @return The value of the property.
+ */
+ getStore: function (key)
+ {
+ return _.activeEngine().get(key);
+ },
+
+ /**
+ * Set a property on the active engine.
+ *
+ * @param key The key of the property to set.
+ *
+ * @param value The value to set the property to.
+ *
+ * @return jQuery
+ */
+ setStore: function (key, value)
+ {
+ _.activeEngine().set(key, value);
+
+ return this;
+ }
+
+ });
+
+ //------------------------------
+ //
+ // Event Bindings
+ //
+ //------------------------------
+
+ //------------------------------
+ //
+ // Startup Code
+ //
+ //------------------------------
+
+ //------------------------------
+ // Expose jStore through jQuery
+ //------------------------------
+
+ window.jStore = $.jStore = _;
+
+ //------------------------------
+ //
+ // Engine Definitions
+ //
+ //------------------------------
+
+ //------------------------------
+ // Local
+ //------------------------------
+
+ define(FLAVOR_LOCAL,
+ {
+ //------------------------------
+ // Properties
+ //------------------------------
+
+ limit: parseInt(5e5, 16),
+
+ //------------------------------
+ // Constructor
+ //------------------------------
+
+ init: function (project, name)
+ {
+ this.database = window.globalStorage === undefined ? window.localStorage : window.globalStorage[location.hostname];
+
+ this._super(project, name);
+ },
+
+ //------------------------------
+ // Methods
+ //------------------------------
+
+ updateCache: function ()
+ {
+ var key, value;
+
+ for (key in this.database)
+ {
+ var has_key = false;
+ if (this.database.hasOwnProperty) {
+ if (this.database.hasOwnProperty(key)) {
+ has_key = true;
+ }
+ } else { // IE 8
+ if (this.database.getItem(key) !== null) {
+ has_key = true;
+ }
+ }
+
+ if (has_key) {
+ value = this.database.getItem(key);
+
+ // Gecko's getItem returns {value: 'the value'}, WebKit returns 'the value'
+ this.data[key] = prepareForRevival(value && value.value ? value.value : value);
+ }
+ }
+
+ this._super();
+ },
+
+ //------------------------------
+ // Internal methods
+ //------------------------------
+
+ __set: function (key, value)
+ {
+ this.database.setItem(key, prepareForStorage(value));
+ },
+
+ __remove: function (key)
+ {
+ this.database.removeItem(key);
+ }
+ },
+
+ function ()
+ {
+ return window.localStorage !== undefined || window.globalStorage !== undefined;
+ });
+
+ //------------------------------
+ // SQL
+ //------------------------------
+
+ define(FLAVOR_SQL,
+ {
+ //------------------------------
+ // Properties
+ //------------------------------
+
+ limit: parseInt(32e3, 16),
+
+ //------------------------------
+ // Constructor
+ //------------------------------
+
+ init: function (project, name)
+ {
+ this.database = window.openDatabase('jstore-' + project, '1.0', project, this.limit);
+
+ if (!this.database)
+ {
+ throw 'JSTORE_SQL_NO_DB';
+ }
+
+ this.database.transaction(function (database)
+ {
+ database.executeSql('CREATE TABLE IF NOT EXISTS jstore (k TEXT UNIQUE NOT NULL PRIMARY KEY, v TEXT NOT NULL)');
+ });
+
+ this._super(project, name);
+ },
+
+ //------------------------------
+ // Methods
+ //------------------------------
+
+ updateCache: function ()
+ {
+ var self = this,
+ _super = this._super;
+
+ this.database.transaction(function (database)
+ {
+ database.executeSql('SELECT k,v FROM jstore', [], function (database, result)
+ {
+ var rows = result.rows,
+ index = 0,
+ row;
+
+ for (; index < rows.length; ++index)
+ {
+ row = rows.item(index);
+ self.data[row.k] = prepareForRevival(row.v);
+ }
+
+ _super.apply(self);
+ });
+ });
+ },
+
+ //------------------------------
+ // Internal methods
+ //------------------------------
+
+ __set: function (key, value)
+ {
+ this.database.transaction(function (database)
+ {
+ database.executeSql('INSERT OR REPLACE INTO jstore(k, v) VALUES (?, ?)', [key, prepareForStorage(value)]);
+ });
+ },
+
+ __remove: function (key)
+ {
+ this.database.transaction(function (database)
+ {
+ database.executeSql('DELETE FROM jstore WHERE k = ?', [key]);
+ });
+ }
+ },
+
+ function ()
+ {
+ return window.openDatabase !== undefined;
+ });
+
+ //------------------------------
+ // Flash
+ //------------------------------
+
+ define(FLAVOR_FLASH,
+ {
+ //------------------------------
+ // Properties
+ //------------------------------
+
+ limit: -1,
+
+ //------------------------------
+ // Constructor
+ //------------------------------
+
+ init: function (project, name)
+ {
+ var self = this;
+
+ _.bind('flash-ready', function ()
+ {
+ self.__flashReadyListener();
+ });
+
+ this._super(project, name);
+ },
+
+ //------------------------------
+ // Methods
+ //------------------------------
+
+ updateCache: function (enable)
+ {
+ /**
+ * The default call to updateCache passes no variable, so we can short circuit the
+ * ready state until we explictly call this after flash ready.
+ */
+ if (enable === true)
+ {
+ var key,
+ dataset = this.database.jstore_get_all();
+
+ for (key in dataset)
+ {
+ if (dataset.hasOwnProperty(key))
+ {
+ this.data[key] = prepareForRevival(this.database.jstore_get(key));
+ }
+ }
+
+ this._super();
+ }
+ },
+
+ //------------------------------
+ // Internal methods
+ //------------------------------
+
+ __set: function (key, value)
+ {
+ if (!this.database.jstore_set(key, prepareForStorage(value)))
+ {
+ _.trigger('jstore-error', ['JSTORE_STORAGE_FAILURE', this.jri, 'Flash Exception']);
+ }
+ },
+
+ __remove: function (key)
+ {
+ this.database.jstore_remove(key);
+ },
+
+ /**
+ * Triggered whenever flash is ready.
+ */
+ __flashReadyListener: function ()
+ {
+ var iFrame = $('#jStoreFlashFrame')[0],
+ frameDocument;
+
+ // MSIE
+ if (iFrame.Document !== undefined && typecheck(iFrame.Document.jStoreFlash.jstore_get, 'Function'))
+ {
+ this.database = iFrame.Document.jStoreFlash;
+ }
+
+ // Real Browsers
+ else if (iFrame.contentWindow && iFrame.contentWindow.document)
+ {
+ frameDocument = $(iFrame.contentWindow.document);
+
+ // Webkit
+ if (typecheck($('object', frameDocument)[0].jstore_get, 'Function'))
+ {
+ this.database = $('object', frameDocument)[0];
+ }
+
+ // Gecko
+ else if (typecheck($('embed', frameDocument)[0].jstore_get, 'Function'))
+ {
+ this.database = $('embed', frameDocument)[0];
+ }
+ }
+
+ if (this.database === undefined)
+ {
+ throw 'JSTORE_FLASH_REFERENCE_ISSUE';
+ }
+ else
+ {
+ this.updateCache(true);
+ }
+ }
+ },
+
+ function ()
+ {
+ return hasFlashVersion('9.0.0');
+ });
+
+ //------------------------------
+ // Gears
+ //------------------------------
+
+ define(FLAVOR_GEARS,
+ {
+ //------------------------------
+ // Properties
+ //------------------------------
+
+ limit: -1,
+
+ //------------------------------
+ // Constructor
+ //------------------------------
+
+ init: function (project, name)
+ {
+ this.database = google.gears.factory.create('beta.database');
+ this.database.open('jstore-' + project);
+ this.database.execute('CREATE TABLE IF NOT EXISTS jstore (k TEXT UNIQUE NOT NULL PRIMARY KEY, v TEXT NOT NULL)');
+
+ this._super(project, name);
+ },
+
+ //------------------------------
+ // Methods
+ //------------------------------
+
+ updateCache: function ()
+ {
+ var result = this.database.execute('SELECT k,v FROM jstore');
+
+ while (result.isValidRow())
+ {
+ this.data[result.field(0)] = prepareForRevival(result.field(1));
+ result.next();
+ }
+
+ result.close();
+
+ this._super();
+ },
+
+ //------------------------------
+ // Internal methods
+ //------------------------------
+
+ __set: function (key, value)
+ {
+ this.database.execute('BEGIN');
+ this.database.execute('INSERT OR REPLACE INTO jstore(k, v) VALUES (?, ?)', [key, prepareForStorage(value)]);
+ this.database.execute('COMMIT');
+ },
+
+ __remove: function (key)
+ {
+ this.database.execute('BEGIN');
+ this.database.execute('DELETE FROM jstore WHERE k = ?', [key]);
+ this.database.execute('COMMIT');
+ }
+ },
+
+ function ()
+ {
+ return window.google !== undefined && window.google.gears !== undefined;
+ });
+
+ //------------------------------
+ // MSIE
+ //------------------------------
+
+ define(FLAVOR_MSIE,
+ {
+ //------------------------------
+ // Properties
+ //------------------------------
+
+ limit: parseInt(1e4, 16),
+
+ //------------------------------
+ // Constructor
+ //------------------------------
+
+ init: function (project, name)
+ {
+ this.database = $('<div style="display:none;behavior:url(\'#default#userData\')" id="jstore-' + project + '"></div>')
+ .appendTo(document.body).get(0);
+
+ this._super(project, name);
+ },
+
+ //------------------------------
+ // Methods
+ //------------------------------
+
+ updateCache: function ()
+ {
+ this.database.load(this.project);
+
+ var node = document.getElementById('jstore-' + this.project),
+ xmlDoc = node.XMLDocument,
+ root,
+ index = 0;
+
+ if (xmlDoc && xmlDoc.documentElement && xmlDoc.documentElement.attributes)
+ {
+ root = xmlDoc.documentElement;
+
+ for (; index < root.attributes.length; ++index)
+ {
+ this.data[root.attributes.item(index).nodeName] = prepareForRevival(root.attributes.item(index).nodeValue);
+ }
+ }
+
+ this._super();
+ },
+
+ //------------------------------
+ // Internal methods
+ //------------------------------
+
+ __set: function (key, value)
+ {
+ this.database.setAttribute(key, prepareForStorage(value));
+ this.database.save(this.project);
+ },
+
+ __remove: function (key)
+ {
+ this.database.removeAttribute(key);
+ this.database.save(this.project);
+ }
+ },
+
+ function ()
+ {
+ return window.ActiveXObject !== undefined;
+ });
+
+}(jQuery, window));
diff -r a9402fc7ce1b -r 5536d57237a3 static/scripts/json2.js
--- a/static/scripts/json2.js Fri Apr 09 13:02:15 2010 -0400
+++ b/static/scripts/json2.js Fri Apr 09 18:09:54 2010 -0400
@@ -1,6 +1,6 @@
/*
http://www.JSON.org/json2.js
- 2009-06-29
+ 2010-03-20
Public Domain.
@@ -8,6 +8,14 @@
See http://www.JSON.org/js.html
+
+ This code should be minified before deployment.
+ See http://javascript.crockford.com/jsmin.html
+
+ USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
+ NOT CONTROL.
+
+
This file creates a global JSON object containing two methods: stringify
and parse.
@@ -33,7 +41,7 @@
value represented by the name/value pair that should be serialized,
or undefined if nothing should be serialized. The toJSON method
will be passed the key associated with the value, and this will be
- bound to the object holding the key.
+ bound to the value
For example, this would serialize Dates as ISO strings.
@@ -136,15 +144,9 @@
This is a reference implementation. You are free to copy, modify, or
redistribute.
-
- This code should be minified before deployment.
- See http://javascript.crockford.com/jsmin.html
-
- USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
- NOT CONTROL.
*/
-/*jslint evil: true */
+/*jslint evil: true, strict: false */
/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
@@ -153,10 +155,13 @@
test, toJSON, toString, valueOf
*/
+
// Create a JSON object only if one does not already exist. We create the
// methods in a closure to avoid creating global variables.
-var JSON = JSON || {};
+if (!this.JSON) {
+ this.JSON = {};
+}
(function () {
@@ -428,6 +433,7 @@
// Unicode characters with escape sequences. JavaScript handles many characters
// incorrectly, either silently deleting them, or treating them as line endings.
+ text = String(text);
cx.lastIndex = 0;
if (cx.test(text)) {
text = text.replace(cx, function (a) {
diff -r a9402fc7ce1b -r 5536d57237a3 static/scripts/packed/class.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/static/scripts/packed/class.js Fri Apr 09 18:09:54 2010 -0400
@@ -0,0 +1,7 @@
+/*
+ * Class definition
+ *
+ * Copyright (c) 2008 John Resig (http://ejohn.org/blog/simple-javascript-inheritance/)
+ * Inspired by base2 and Prototype
+ */
+"use strict";(function(){var a=false,b=/xyz/.test(function(){xyz})?/\b_super\b/:/.*/;this.Class=function(){};Class.extend=function(g){var f=this.prototype;a=true;var e=new this();a=false;for(var d in g){e[d]=(typeof g[d]==="function"&&typeof f[d]==="function"&&b.test(g[d])?(function(h,i){return function(){var k=this._super;this._super=f[h];var j=i.apply(this,arguments);this._super=k;return j}}(d,g[d])):g[d])}function c(){if(!a&&this.init){this.init.apply(this,arguments)}}c.prototype=e;c.constructor=c;c.extend=arguments.callee;return c}}());
\ No newline at end of file
diff -r a9402fc7ce1b -r 5536d57237a3 static/scripts/packed/galaxy.base.js
--- a/static/scripts/packed/galaxy.base.js Fri Apr 09 13:02:15 2010 -0400
+++ b/static/scripts/packed/galaxy.base.js Fri Apr 09 18:09:54 2010 -0400
@@ -1,1 +1,1 @@
-$(document).ready(function(){replace_big_select_inputs()});$.fn.makeAbsolute=function(a){return this.each(function(){var b=$(this);var c=b.position();b.css({position:"absolute",marginLeft:0,marginTop:0,top:c.top,left:c.left,right:$(window).width()-(c.left+b.width())});if(a){b.remove().appendTo("body")}})};function ensure_popup_helper(){if($("#popup-helper").length===0){$("<div id='popup-helper'/>").css({background:"white",opacity:0,zIndex:15000,position:"absolute",top:0,left:0,width:"100%",height:"100%"}).appendTo("body").hide()}}function attach_popupmenu(b,d){var a=function(){d.unbind().hide();$("#popup-helper").unbind("click.popupmenu").hide()};var c=function(g){$("#popup-helper").bind("click.popupmenu",a).show();d.click(a).css({left:0,top:-1000}).show();var f=g.pageX-d.width()/2;f=Math.min(f,$(document).scrollLeft()+$(window).width()-$(d).width()-20);f=Math.max(f,$(document).scrollLeft()+20);d.css({top:g.pageY-5,left:f});return false};$(b).click(c)}function make_popupmen!
u(c,b){ensure_popup_helper();var a=$("<ul id='"+c.attr("id")+"-menu'></ul>");$.each(b,function(f,e){if(e){$("<li/>").html(f).click(e).appendTo(a)}else{$("<li class='head'/>").html(f).appendTo(a)}});var d=$("<div class='popmenu-wrapper'>");d.append(a).append("<div class='overlay-border'>").css("position","absolute").appendTo("body").hide();attach_popupmenu(c,d)}function make_popup_menus(){jQuery("div[popupmenu]").each(function(){var c={};$(this).find("a").each(function(){var b=$(this).attr("confirm"),d=$(this).attr("href"),e=$(this).attr("target");c[$(this).text()]=function(){if(!b||confirm(b)){var g=window;if(e=="_parent"){g=window.parent}else{if(e=="_top"){g=window.top}}g.location=d}}});var a=$("#"+$(this).attr("popupmenu"));make_popupmenu(a,c);$(this).remove();a.addClass("popup").show()})}function array_length(b){if(b.length){return b.length}var c=0;for(var a in b){c++}return c}function replace_big_select_inputs(){$("select[name=dbkey]").each(function(){var a=$(this);if(a!
.find("option").length<20){return}var b=a.attr("value");var c=$("<inpu
t type='text' class='text-and-autocomplete-select'></input>");c.attr("size",40);c.attr("name",a.attr("name"));c.click(function(){var g=$(this).attr("value");$(this).attr("value","Loading...");$(this).showAllInCache();$(this).attr("value",g);$(this).select()});var f=[];var e={};a.children("option").each(function(){var h=$(this).text();var g=$(this).attr("value");if(g=="?"){return}f.push(h);e[h]=g;e[g]=g;if(g==b){c.attr("value",h)}});if(c.attr("value")==""){c.attr("value","Click to Search or Select")}var d={selectFirst:false,autoFill:false,mustMatch:false,matchContains:true,max:1000,minChars:0,hideForLessThanMinChars:false};c.autocomplete(f,d);a.replaceWith(c);c.parents("form").submit(function(){var h=c.attr("value");var g=e[h];if(g!==null&&g!==undefined){c.attr("value",g)}else{if(b!=""){c.attr("value",b)}else{c.attr("value","?")}}})})}function async_save_text(d,f,e,a,c,h,i,g,b){if(c===undefined){c=30}if(i===undefined){i=4}$("#"+d).live("click",function(){if($("#renaming-activ!
e").length>0){return}var l=$("#"+f),k=l.text(),j;if(h){j=$("<textarea></textarea>").attr({rows:i,cols:c}).text(k)}else{j=$("<input type='text'></input>").attr({value:k,size:c})}j.attr("id","renaming-active");j.blur(function(){$(this).remove();l.show();if(b){b(j)}});j.keyup(function(n){if(n.keyCode===27){$(this).trigger("blur")}else{if(n.keyCode===13){var m={};m[a]=$(this).val();$(this).trigger("blur");$.ajax({url:e,data:m,error:function(){alert("Text editing for elt "+f+" failed")},success:function(o){l.text(o);if(b){b(j)}}})}}});if(g){g(j)}l.hide();j.insertAfter(l);j.focus();j.select();return})}$(document).ready(function(){$("a[confirm]").click(function(){return confirm($(this).attr("confirm"))});if($.fn.tipsy){$(".tooltip").tipsy({gravity:"s"})}make_popup_menus()});
\ No newline at end of file
+$(document).ready(function(){replace_big_select_inputs()});$.fn.makeAbsolute=function(a){return this.each(function(){var b=$(this);var c=b.position();b.css({position:"absolute",marginLeft:0,marginTop:0,top:c.top,left:c.left,right:$(window).width()-(c.left+b.width())});if(a){b.remove().appendTo("body")}})};function ensure_popup_helper(){if($("#popup-helper").length===0){$("<div id='popup-helper'/>").css({background:"white",opacity:0,zIndex:15000,position:"absolute",top:0,left:0,width:"100%",height:"100%"}).appendTo("body").hide()}}function attach_popupmenu(b,d){var a=function(){d.unbind().hide();$("#popup-helper").unbind("click.popupmenu").hide()};var c=function(g){$("#popup-helper").bind("click.popupmenu",a).show();d.click(a).css({left:0,top:-1000}).show();var f=g.pageX-d.width()/2;f=Math.min(f,$(document).scrollLeft()+$(window).width()-$(d).width()-20);f=Math.max(f,$(document).scrollLeft()+20);d.css({top:g.pageY-5,left:f});return false};$(b).click(c)}function make_popupmen!
u(c,b){ensure_popup_helper();var a=$("<ul id='"+c.attr("id")+"-menu'></ul>");$.each(b,function(f,e){if(e){$("<li/>").html(f).click(e).appendTo(a)}else{$("<li class='head'/>").html(f).appendTo(a)}});var d=$("<div class='popmenu-wrapper'>");d.append(a).append("<div class='overlay-border'>").css("position","absolute").appendTo("body").hide();attach_popupmenu(c,d)}function make_popup_menus(){jQuery("div[popupmenu]").each(function(){var c={};$(this).find("a").each(function(){var b=$(this).attr("confirm"),d=$(this).attr("href"),e=$(this).attr("target");c[$(this).text()]=function(){if(!b||confirm(b)){var g=window;if(e=="_parent"){g=window.parent}else{if(e=="_top"){g=window.top}}g.location=d}}});var a=$("#"+$(this).attr("popupmenu"));make_popupmenu(a,c);$(this).remove();a.addClass("popup").show()})}function array_length(b){if(b.length){return b.length}var c=0;for(var a in b){c++}return c}function replace_big_select_inputs(){$("select[name=dbkey]").each(function(){var a=$(this);if(a!
.find("option").length<20){return}var b=a.attr("value");var c=$("<inpu
t type='text' class='text-and-autocomplete-select'></input>");c.attr("size",40);c.attr("name",a.attr("name"));c.click(function(){var g=$(this).attr("value");$(this).attr("value","Loading...");$(this).showAllInCache();$(this).attr("value",g);$(this).select()});var f=[];var e={};a.children("option").each(function(){var h=$(this).text();var g=$(this).attr("value");if(g=="?"){return}f.push(h);e[h]=g;e[g]=g;if(g==b){c.attr("value",h)}});if(c.attr("value")==""){c.attr("value","Click to Search or Select")}var d={selectFirst:false,autoFill:false,mustMatch:false,matchContains:true,max:1000,minChars:0,hideForLessThanMinChars:false};c.autocomplete(f,d);a.replaceWith(c);c.parents("form").submit(function(){var h=c.attr("value");var g=e[h];if(g!==null&&g!==undefined){c.attr("value",g)}else{if(b!=""){c.attr("value",b)}else{c.attr("value","?")}}})})}function async_save_text(d,f,e,a,c,h,i,g,b){if(c===undefined){c=30}if(i===undefined){i=4}$("#"+d).live("click",function(){if($("#renaming-activ!
e").length>0){return}var l=$("#"+f),k=l.text(),j;if(h){j=$("<textarea></textarea>").attr({rows:i,cols:c}).text(k)}else{j=$("<input type='text'></input>").attr({value:k,size:c})}j.attr("id","renaming-active");j.blur(function(){$(this).remove();l.show();if(b){b(j)}});j.keyup(function(n){if(n.keyCode===27){$(this).trigger("blur")}else{if(n.keyCode===13){var m={};m[a]=$(this).val();$(this).trigger("blur");$.ajax({url:e,data:m,error:function(){alert("Text editing for elt "+f+" failed")},success:function(o){l.text(o);if(b){b(j)}}})}}});if(g){g(j)}l.hide();j.insertAfter(l);j.focus();j.select();return})}function init_history_items(c,a){var b=function(){try{var d=$.jStore.store("history_expand_state");if(d){for(var f in d){$("#"+f+" div.historyItemBody").show()}}}catch(e){$.jStore.remove("history_expand_state")}if($.browser.mozilla){$("div.historyItemBody").each(function(){if(!$(this).is(":visible")){$(this).find("pre.peek").css("overflow","hidden")}})}c.each(function(){var i=this.i!
d;var g=$(this).children("div.historyItemBody");var h=g.find("pre.peek
");$(this).children(".historyItemTitleBar").find(".historyItemTitle").wrap("<a href='#'></a>").click(function(){if(g.is(":visible")){if($.browser.mozilla){h.css("overflow","hidden")}g.slideUp("fast");if(!a){var j=$.jStore.store("history_expand_state");if(j){delete j[i];$.jStore.store("history_expand_state",j)}}}else{g.slideDown("fast",function(){if($.browser.mozilla){h.css("overflow","auto")}});if(!a){var j=$.jStore.store("history_expand_state");if(j===undefined){j={}}j[i]=true;$.jStore.store("history_expand_state",j)}}return false})});$("#top-links > a.toggle").click(function(){var g=$.jStore.store("history_expand_state");if(g===undefined){g={}}$("div.historyItemBody:visible").each(function(){if($.browser.mozilla){$(this).find("pre.peek").css("overflow","hidden")}$(this).slideUp("fast");if(g){delete g[$(this).parent().attr("id")]}});$.jStore.store("history_expand_state",g)}).show()};if(a){b()}else{$.jStore.init("galaxy");$.jStore.engineReady(function(){b()})}}$(document).re!
ady(function(){$("a[confirm]").click(function(){return confirm($(this).attr("confirm"))});if($.fn.tipsy){$(".tooltip").tipsy({gravity:"s"})}make_popup_menus()});
\ No newline at end of file
diff -r a9402fc7ce1b -r 5536d57237a3 static/scripts/packed/jquery.jstore-all.js
--- a/static/scripts/packed/jquery.jstore-all.js Fri Apr 09 13:02:15 2010 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-(function(){var a=false,b=/xyz/.test(function(){xyz})?/\b_super\b/:/.*/;this.Class=function(){};Class.extend=function(g){var f=this.prototype;a=true;var e=new this();a=false;for(var d in g){e[d]=typeof g[d]=="function"&&typeof f[d]=="function"&&b.test(g[d])?(function(h,i){return function(){var k=this._super;this._super=f[h];var j=i.apply(this,arguments);this._super=k;return j}})(d,g[d]):g[d]}function c(){if(!a&&this.init){this.init.apply(this,arguments)}}c.prototype=e;c.constructor=c;c.extend=arguments.callee;return c}})();(function(a){this.jStoreDelegate=Class.extend({init:function(b){this.parent=b;this.callbacks={}},bind:function(b,c){if(!a.isFunction(c)){return this}if(!this.callbacks[b]){this.callbacks[b]=[]}this.callbacks[b].push(c);return this},trigger:function(){var d=this.parent,c=[].slice.call(arguments),e=c.shift(),b=this.callbacks[e];if(!b){return false}a.each(b,function(){this.apply(d,c)});return this}})})(jQuery);(function(a){a.jStore={};a.extend(a.jStore,{Engi!
neOrder:[],Availability:{},Engines:{},Instances:{},CurrentEngine:null,defaults:{project:null,engine:null,autoload:true,flash:"jStore.Flash.html"},isReady:false,isFlashReady:false,delegate:new jStoreDelegate(a.jStore).bind("jStore-ready",function(b){a.jStore.isReady=true;if(a.jStore.defaults.autoload){b.connect()}}).bind("flash-ready",function(){a.jStore.isFlashReady=true})});a.jStore.ready=function(b){if(a.jStore.isReady){b.apply(a.jStore,[a.jStore.CurrentEngine])}else{a.jStore.delegate.bind("jStore-ready",b)}};a.jStore.fail=function(b){a.jStore.delegate.bind("jStore-failure",b)};a.jStore.flashReady=function(b){if(a.jStore.isFlashReady){b.apply(a.jStore,[a.jStore.CurrentEngine])}else{a.jStore.delegate.bind("flash-ready",b)}};a.jStore.use=function(d,g,c){g=g||a.jStore.defaults.project||location.hostname.replace(/\./g,"-")||"unknown";var f=a.jStore.Engines[d.toLowerCase()]||null,b=(c?c+".":"")+g+"."+d;if(!f){throw"JSTORE_ENGINE_UNDEFINED"}f=new f(g,b);if(a.jStore.Instances[b]!
){throw"JSTORE_JRI_CONFLICT"}if(f.isAvailable()){a.jStore.Instances[b]
=f;if(!a.jStore.CurrentEngine){a.jStore.CurrentEngine=f}a.jStore.delegate.trigger("jStore-ready",f)}else{if(!f.autoload){throw"JSTORE_ENGINE_UNAVILABLE"}else{f.included(function(){if(this.isAvailable()){a.jStore.Instances[b]=this;if(!a.jStore.CurrentEngine){a.jStore.CurrentEngine=this}a.jStore.delegate.trigger("jStore-ready",this)}else{a.jStore.delegate.trigger("jStore-failure",this)}}).include()}}};a.jStore.setCurrentEngine=function(b){if(!a.jStore.Instances.length){return a.jStore.FindEngine()}if(!b&&a.jStore.Instances.length>=1){a.jStore.delegate.trigger("jStore-ready",a.jStore.Instances[0]);return a.jStore.CurrentEngine=a.jStore.Instances[0]}if(b&&a.jStore.Instances[b]){a.jStore.delegate.trigger("jStore-ready",a.jStore.Instances[b]);return a.jStore.CurrentEngine=a.jStore.Instances[b]}throw"JSTORE_JRI_NO_MATCH"};a.jStore.FindEngine=function(){a.each(a.jStore.EngineOrder,function(b){if(a.jStore.Availability[this]()){a.jStore.use(this,a.jStore.defaults.project,"default");re!
turn false}})};a.jStore.store=function(b,c){if(!a.jStore.CurrentEngine){return false}if(!c){return a.jStore.CurrentEngine.get(b)}return a.jStore.CurrentEngine.set(b,c)};a.jStore.remove=function(b){if(!a.jStore.CurrentEngine){return false}return a.jStore.CurrentEngine.rem(b)};a.fn.store=function(c,d){if(!a.jStore.CurrentEngine){return this}var b=a.jStore.store(c,d);return !d?b:this};a.fn.removeStore=function(b){a.jStore.remove(b);return this};a.jStore.load=function(){if(a.jStore.defaults.engine){return a.jStore.use(a.jStore.defaults.engine,a.jStore.defaults.project,"default")}try{a.jStore.FindEngine()}catch(b){}}})(jQuery);(function(a){this.StorageEngine=Class.extend({init:function(c,b){this.project=c;this.jri=b;this.data={};this.limit=-1;this.includes=[];this.delegate=new jStoreDelegate(this).bind("engine-ready",function(){this.isReady=true}).bind("engine-included",function(){this.hasIncluded=true});this.autoload=false;this.isReady=false;this.hasIncluded=false},include:func!
tion(){var b=this,d=this.includes.length,c=0;a.each(this.includes,func
tion(){a.ajax({type:"get",url:this,dataType:"script",cache:true,success:function(){c++;if(c==d){b.delegate.trigger("engine-included")}}})})},isAvailable:function(){return false},ready:function(b){if(this.isReady){b.apply(this)}else{this.delegate.bind("engine-ready",b)}return this},included:function(b){if(this.hasIncluded){b.apply(this)}else{this.delegate.bind("engine-included",b)}return this},get:function(b){return this.data[b]||null},set:function(b,c){this.data[b]=c;return c},rem:function(b){var c=this.data[b];this.data[b]=null;return c}})})(jQuery);(function(c){var b=c.jStore.Availability.session=function(){return !!window.sessionStorage},a=c.jStore.Availability.local=function(){return !!(window.localStorage||window.globalStorage)};this.jStoreDom=StorageEngine.extend({init:function(e,d){this._super(e,d);this.type="DOM";this.limit=5*1024*1024},connect:function(){this.delegate.trigger("engine-ready")},get:function(e){var d=this.db.getItem(e);return d&&d.value?d.value:d},set:!
function(d,e){this.db.setItem(d,e);return e},rem:function(e){var d=this.get(e);this.db.removeItem(e);return d}});this.jStoreLocal=jStoreDom.extend({connect:function(){this.db=!window.globalStorage?window.localStorage:window.globalStorage[location.hostname];this._super()},isAvailable:a});this.jStoreSession=jStoreDom.extend({connect:function(){this.db=sessionStorage;this._super()},isAvailable:b});c.jStore.Engines.local=jStoreLocal;c.jStore.Engines.session=jStoreSession;c.jStore.EngineOrder[1]="local"})(jQuery);(function(b){var a=b.jStore.Availability.flash=function(){return !!(b.jStore.hasFlash("8.0.0"))};this.jStoreFlash=StorageEngine.extend({init:function(e,d){this._super(e,d);this.type="Flash";var c=this;b.jStore.flashReady(function(){c.flashReady()})},connect:function(){var c="jstore-flash-embed-"+this.project;b(document.body).append('<iframe style="height:1px;width:1px;position:absolute;left:0;top:0;margin-left:-100px;" id="jStoreFlashFrame" src="'+b.jStore.defaults.flas!
h+'"></iframe>')},flashReady:function(f){var c=b("#jStoreFlashFrame")[
0];if(c.Document&&b.isFunction(c.Document.jStoreFlash.f_get_cookie)){this.db=c.Document.jStoreFlash}else{if(c.contentWindow&&c.contentWindow.document){var d=c.contentWindow.document;if(b.isFunction(b("object",b(d))[0].f_get_cookie)){this.db=b("object",b(d))[0]}else{if(b.isFunction(b("embed",b(d))[0].f_get_cookie)){this.db=b("embed",b(d))[0]}}}}if(this.db){this.delegate.trigger("engine-ready")}},isAvailable:a,get:function(d){var c=this.db.f_get_cookie(d);return c=="null"?null:c},set:function(c,d){this.db.f_set_cookie(c,d);return d},rem:function(c){var d=this.get(c);this.db.f_delete_cookie(c);return d}});b.jStore.Engines.flash=jStoreFlash;b.jStore.EngineOrder[2]="flash";b.jStore.hasFlash=function(c){var e=b.jStore.flashVersion().match(/\d+/g),f=c.match(/\d+/g);for(var d=0;d<3;d++){e[d]=parseInt(e[d]||0);f[d]=parseInt(f[d]||0);if(e[d]<f[d]){return false}if(e[d]>f[d]){return true}}return true};b.jStore.flashVersion=function(){try{try{var c=new ActiveXObject("ShockwaveFlash.Shock!
waveFlash.6");try{c.AllowScriptAccess="always"}catch(d){return"6,0,0"}}catch(d){}return new ActiveXObject("ShockwaveFlash.ShockwaveFlash").GetVariable("$version").replace(/\D+/g,",").match(/^,?(.+),?$/)[1]}catch(d){try{if(navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin){return(navigator.plugins["Shockwave Flash 2.0"]||navigator.plugins["Shockwave Flash"]).description.replace(/\D+/g,",").match(/^,?(.+),?$/)[1]}}catch(d){}}return"0,0,0"}})(jQuery);function flash_ready(){$.jStore.delegate.trigger("flash-ready")}(function(b){var a=b.jStore.Availability.gears=function(){return !!(window.google&&window.google.gears)};this.jStoreGears=StorageEngine.extend({init:function(d,c){this._super(d,c);this.type="Google Gears";this.includes.push("http://code.google.com/apis/gears/gears_init.js");this.autoload=true},connect:function(){var c=this.db=google.gears.factory.create("beta.database");c.open("jstore-"+this.project);c.execute("CREATE TABLE IF NOT EXISTS jstore (k TEX!
T UNIQUE NOT NULL PRIMARY KEY, v TEXT NOT NULL)");this.updateCache()},
updateCache:function(){var c=this.db.execute("SELECT k,v FROM jstore");while(c.isValidRow()){this.data[c.field(0)]=c.field(1);c.next()}c.close();this.delegate.trigger("engine-ready")},isAvailable:a,set:function(d,e){var c=this.db;c.execute("BEGIN");c.execute("INSERT OR REPLACE INTO jstore(k, v) VALUES (?, ?)",[d,e]);c.execute("COMMIT");return this._super(d,e)},rem:function(d){var c=this.db;c.execute("BEGIN");c.execute("DELETE FROM jstore WHERE k = ?",[d]);c.execute("COMMIT");return this._super(d)}});b.jStore.Engines.gears=jStoreGears;b.jStore.EngineOrder[3]="gears"})(jQuery);(function(b){var a=b.jStore.Availability.html5=function(){return !!window.openDatabase};this.jStoreHtml5=StorageEngine.extend({init:function(d,c){this._super(d,c);this.type="HTML5";this.limit=1024*200},connect:function(){var c=this.db=openDatabase("jstore-"+this.project,"1.0",this.project,this.limit);if(!c){throw"JSTORE_ENGINE_HTML5_NODB"}c.transaction(function(d){d.executeSql("CREATE TABLE IF NOT EXISTS!
jstore (k TEXT UNIQUE NOT NULL PRIMARY KEY, v TEXT NOT NULL)")});this.updateCache()},updateCache:function(){var c=this;this.db.transaction(function(d){d.executeSql("SELECT k,v FROM jstore",[],function(f,e){var h=e.rows,g=0,j;for(;g<h.length;++g){j=h.item(g);c.data[j.k]=j.v}c.delegate.trigger("engine-ready")})})},isAvailable:a,set:function(c,d){this.db.transaction(function(e){e.executeSql("INSERT OR REPLACE INTO jstore(k, v) VALUES (?, ?)",[c,d])});return this._super(c,d)},rem:function(c){this.db.transaction(function(d){d.executeSql("DELETE FROM jstore WHERE k = ?",[c])});return this._super(c)}});b.jStore.Engines.html5=jStoreHtml5;b.jStore.EngineOrder[0]="html5"})(jQuery);(function(b){var a=b.jStore.Availability.ie=function(){return !!window.ActiveXObject};this.jStoreIE=StorageEngine.extend({init:function(d,c){this._super(d,c);this.type="IE";this.limit=64*1024},connect:function(){this.db=b('<div style="display:none;behavior:url(\'#default#userData\')" id="jstore-'+this.proj!
ect+'"></div>').appendTo(document.body).get(0);this.delegate.trigger("
engine-ready")},isAvailable:a,get:function(c){this.db.load(this.project);return this.db.getAttribute(c)},set:function(c,d){this.db.setAttribute(c,d);this.db.save(this.project);return d},rem:function(c){var d=this.get(c);this.db.removeAttribute(c);this.db.save(this.project);return d}});b.jStore.Engines.ie=jStoreIE;b.jStore.EngineOrder[4]="ie"})(jQuery);
\ No newline at end of file
diff -r a9402fc7ce1b -r 5536d57237a3 static/scripts/packed/jquery.jstore.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/static/scripts/packed/jquery.jstore.js Fri Apr 09 18:09:54 2010 -0400
@@ -0,0 +1,17 @@
+/*
+ * jStore 2.0 - Persistent Client Side Storage
+ *
+ * Copyright (c) 2010 Eric Garside (http://eric.garside.name/)
+ * Dual licensed under:
+ * MIT: http://www.opensource.org/licenses/mit-license.php
+ * GPLv3: http://www.opensource.org/licenses/gpl-3.0.html
+ *
+ * ---------------------------
+ *
+ * jStore Flash Storage Component
+ *
+ * Copyright (c) 2006 Jeff Lerman (jeff(a)blip.tv)
+ * Licensed under the Creative Commons Attribution 3.0 United States License:
+ * http://creativecommons.org/licenses/by/3.0/us
+ */
+"use strict";(function(i,n){var h="JSTORE_ENGINE_UNSTABLE",t="JSTORE_UNKNOWN_ENGINE_REQUESTED",r="JSTORE_ENGINE_NAMESPACE_COLLISION",j="JSTORE_RESOURCE_NAMESPACE_COLLISION",m="JSTORE_ENGINE_UNAVAILABLE",o="JSTORE_INVALID_FLAVOR",f=(function(){try{return new RegExp('^("(\\\\.|[^"\\\\\\n\\r])*?"|[,:{}\\[\\]0-9.\\-+Eaeflnr-u \\n\\r\\t])+?$')}catch(I){return(/^(true|false|null|\[.*\]|\{.*\}|".*"|\d+|\d+\.\d+)$/)}}()),b="jstore-html5-local",y="jstore-html5-sql",F="jstore-flash",G="jstore-google-gears",A="jstore-msie",u,E={},D={},C={},c=false,d={},q={project:undefined,flash:"jStore.Flash.html",json:"browser.json.js"},l;function z(I){switch(I){case b:case y:case F:case G:case A:return true;default:return false}}function g(I,J){return !I?false:I.constructor.toString().match(new RegExp(J+"\\(\\)","i"))!==null}function s(K,I,J){if(g(K,"Function")){return K.apply(I||E,g(J,"Array")?J:[J])}}function p(J,I){i.ajax({url:J,complete:I||i.noop(),type:"GET",dataType:"script",cache:false})}fun!
ction x(I){if(I===undefined){return""}if(g(I,"Object")||g(I,"Array")||g(I,"Function")){return JSON.stringify(I)}return I}function k(I){return f.test(I)?JSON.parse(I):I}function w(I){return I.replace(/^\s+|\s+$/g,"")}function e(I,J,K){if(!z(I)){throw o}if(K[I]!==undefined){throw r}if(s(K)===true){E.available[I]=true;J.flavor=I;D[I]=u.extend(J)}else{E.available[I]=false;E.enginePriority=i.map(E.enginePriority,function(L){if(L===I){return null}else{return L}})}}function v(){if(E.isReady){return}if((c&&E.isFlashReady)||!c){E.isReady=true;E.trigger("jstore-ready",[C[l]])}}function a(){E.create(E.enginePriority[0],undefined,"best-fit")}function H(){try{var J=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");try{J.AllowScriptAccess="always"}catch(L){return"6,0,0"}return new ActiveXObject("ShockwaveFlash.ShockwaveFlash").GetVariable("$version").replace(/\D+/g,",").match(/^,?(.+),?$/)[1]}catch(K){try{if(navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin){return(na!
vigator.plugins["Shockwave Flash 2.0"]||navigator.plugins["Shockwave F
lash"]).description.replace(/\D+/g,",").match(/^,?(.+),?$/)[1]}}catch(I){}}return"0,0,0"}function B(I){var N=H().match(/\d+/g),L=I.match(/\d+/g),J=0,K,M;for(;J<3;J++){K=parseInt(N[J],10);M=parseInt(L[J],10);if(K<M){return false}else{if(K>M){return true}}}return true}i.extend(E,{enginePriority:[b,y,F,A],available:{},isReady:false,isFlashReady:false,flavors:{local:b,sql:y,flash:F,gears:G,msie:A},init:function(J,K,I){i.extend(q,{project:J},K);i(function(){if(n.JSON===undefined){p(q.json)}if(I!==undefined){E.create(I,J,"default")}else{a()}});return E},create:function(I,M,J){M=M||q.project||location.hostname.replace(/\./g,"-")||"unknown";if(!z(I)){throw o}if(D[I]===undefined){throw m}var L=(J!==undefined?J+".":"")+M+"."+I,K;if(C[L]!==undefined){throw j}K=C[L]=new D[I](M,L);K.ready(function(){E.trigger("jstore-engine-ready",[K])});if(I===F&&!E.isFlashReady){if(l===undefined){c=true}n.jstore_ready=function(){E.isFlashReady=true;E.trigger("flash-ready");if(l===undefined){v()}n.flash!
_ready=undefined};n.jstore_error=function(N){E.trigger("jstore-error",["JSTORE_FLASH_EXCEPTION",null,N])};i('<iframe style="height:1px;width:1px;position:absolute;left:0;top:0;margin-left:-100px;" id="jStoreFlashFrame" src="'+q.flash+'"></iframe>').appendTo("body")}else{if(l===undefined){l=L;v()}}return K},engine:function(I){return C[I]},activeEngine:function(I){if(I!==undefined){if(C[I]===undefined){throw t}else{l=I}}return C[l]},bind:function(J,K,I){I=I||"jstore";if(d[I]===undefined){d[I]={}}if(d[I][J]===undefined){d[I][J]=[K]}else{d[I][J].push(K)}return E},trigger:function(K,J,I){I=I||"jstore";if(d[I]!==undefined){if(d[I][K]!==undefined){i.each(d[I][K],function(){s(this,E,J)})}}return E},error:function(I){E.bind("jstore-error",I)},ready:function(I){if(E.isReady){s(I)}else{E.bind("jstore-ready",I)}return E},engineReady:function(I){if(E.isReady){s(I)}else{E.bind("jstore-engine-ready",I)}return E},store:function(I,J){return J===undefined?E.get(I):E.set(I,J)},remove:function!
(I){return E.activeEngine().remove(I)},get:function(I){return E.active
Engine().get(I)},set:function(I,J){return E.activeEngine().set(I,J)}});u=Class.extend({project:undefined,jri:undefined,flavor:undefined,database:undefined,data:undefined,limit:undefined,isReady:undefined,init:function(J,I){this.project=J;this.jri=I;this.data={};this.isReady=false;this.updateCache()},updateCache:function(){this.isReady=true;this.trigger("engine-ready",[this])},bind:function(I,J){E.bind(I,J,this.jri)},trigger:function(J,I){E.trigger(J,I,this.jri)},ready:function(I){if(this.isReady){s(I,this)}else{this.bind("engine-ready",I)}},get:function(I){this.__interruptAccess();return this.data[I]},set:function(I,J){this.__interruptAccess();I=w(I);try{this.__set(I,J)}catch(K){E.trigger("jstore-error",["JSTORE_STORAGE_FAILURE",this.jri,K])}this.data[I]=J;return J},remove:function(J){this.__interruptAccess();J=w(J);try{this.__remove(J)}catch(K){E.trigger("jstore-error",["JSTORE_REMOVE_FAILURE",this.jri,K])}var I=this.data[J];this.data[J]=undefined;return I},__interruptAcces!
s:function(){if(!this.isReady){throw h}},__set:function(I,J){return},__remove:function(I){return}});i.extend(i.fn,{store:function(I,J){if(J===undefined){E.get(I)}else{E.set(I,J)}return this},removeStore:function(I){E.activeEngine().remove(I);return this},getStore:function(I){return E.activeEngine().get(I)},setStore:function(I,J){E.activeEngine().set(I,J);return this}});n.jStore=i.jStore=E;e(b,{limit:parseInt(500000,16),init:function(J,I){this.database=n.globalStorage===undefined?n.localStorage:n.globalStorage[location.hostname];this._super(J,I)},updateCache:function(){var J,K;for(J in this.database){var I=false;if(this.database.hasOwnProperty){if(this.database.hasOwnProperty(J)){I=true}}else{if(this.database.getItem(J)!==null){I=true}}if(I){K=this.database.getItem(J);this.data[J]=k(K&&K.value?K.value:K)}}this._super()},__set:function(I,J){this.database.setItem(I,x(J))},__remove:function(I){this.database.removeItem(I)}},function(){return n.localStorage!==undefined||n.globalS!
torage!==undefined});e(y,{limit:parseInt(32000,16),init:function(J,I){
this.database=n.openDatabase("jstore-"+J,"1.0",J,this.limit);if(!this.database){throw"JSTORE_SQL_NO_DB"}this.database.transaction(function(K){K.executeSql("CREATE TABLE IF NOT EXISTS jstore (k TEXT UNIQUE NOT NULL PRIMARY KEY, v TEXT NOT NULL)")});this._super(J,I)},updateCache:function(){var I=this,J=this._super;this.database.transaction(function(K){K.executeSql("SELECT k,v FROM jstore",[],function(O,L){var N=L.rows,M=0,P;for(;M<N.length;++M){P=N.item(M);I.data[P.k]=k(P.v)}J.apply(I)})})},__set:function(I,J){this.database.transaction(function(K){K.executeSql("INSERT OR REPLACE INTO jstore(k, v) VALUES (?, ?)",[I,x(J)])})},__remove:function(I){this.database.transaction(function(J){J.executeSql("DELETE FROM jstore WHERE k = ?",[I])})}},function(){return n.openDatabase!==undefined});e(F,{limit:-1,init:function(K,J){var I=this;E.bind("flash-ready",function(){I.__flashReadyListener()});this._super(K,J)},updateCache:function(I){if(I===true){var J,K=this.database.jstore_get_all();f!
or(J in K){if(K.hasOwnProperty(J)){this.data[J]=k(this.database.jstore_get(J))}}this._super()}},__set:function(I,J){if(!this.database.jstore_set(I,x(J))){E.trigger("jstore-error",["JSTORE_STORAGE_FAILURE",this.jri,"Flash Exception"])}},__remove:function(I){this.database.jstore_remove(I)},__flashReadyListener:function(){var I=i("#jStoreFlashFrame")[0],J;if(I.Document!==undefined&&g(I.Document.jStoreFlash.jstore_get,"Function")){this.database=I.Document.jStoreFlash}else{if(I.contentWindow&&I.contentWindow.document){J=i(I.contentWindow.document);if(g(i("object",J)[0].jstore_get,"Function")){this.database=i("object",J)[0]}else{if(g(i("embed",J)[0].jstore_get,"Function")){this.database=i("embed",J)[0]}}}}if(this.database===undefined){throw"JSTORE_FLASH_REFERENCE_ISSUE"}else{this.updateCache(true)}}},function(){return B("9.0.0")});e(G,{limit:-1,init:function(J,I){this.database=google.gears.factory.create("beta.database");this.database.open("jstore-"+J);this.database.execute("CREA!
TE TABLE IF NOT EXISTS jstore (k TEXT UNIQUE NOT NULL PRIMARY KEY, v T
EXT NOT NULL)");this._super(J,I)},updateCache:function(){var I=this.database.execute("SELECT k,v FROM jstore");while(I.isValidRow()){this.data[I.field(0)]=k(I.field(1));I.next()}I.close();this._super()},__set:function(I,J){this.database.execute("BEGIN");this.database.execute("INSERT OR REPLACE INTO jstore(k, v) VALUES (?, ?)",[I,x(J)]);this.database.execute("COMMIT")},__remove:function(I){this.database.execute("BEGIN");this.database.execute("DELETE FROM jstore WHERE k = ?",[I]);this.database.execute("COMMIT")}},function(){return n.google!==undefined&&n.google.gears!==undefined});e(A,{limit:parseInt(10000,16),init:function(J,I){this.database=i('<div style="display:none;behavior:url(\'#default#userData\')" id="jstore-'+J+'"></div>').appendTo(document.body).get(0);this._super(J,I)},updateCache:function(){this.database.load(this.project);var K=document.getElementById("jstore-"+this.project),L=K.XMLDocument,I,J=0;if(L&&L.documentElement&&L.documentElement.attributes){I=L.document!
Element;for(;J<I.attributes.length;++J){this.data[I.attributes.item(J).nodeName]=k(I.attributes.item(J).nodeValue)}}this._super()},__set:function(I,J){this.database.setAttribute(I,x(J));this.database.save(this.project)},__remove:function(I){this.database.removeAttribute(I);this.database.save(this.project)}},function(){return n.ActiveXObject!==undefined})}(jQuery,window));
\ No newline at end of file
diff -r a9402fc7ce1b -r 5536d57237a3 static/scripts/packed/json2.js
--- a/static/scripts/packed/json2.js Fri Apr 09 13:02:15 2010 -0400
+++ b/static/scripts/packed/json2.js Fri Apr 09 18:09:54 2010 -0400
@@ -1,1 +1,1 @@
-var JSON=JSON||{};(function(){function f(n){return n<10?"0"+n:n}if(typeof Date.prototype.toJSON!=="function"){Date.prototype.toJSON=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf()}}var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==="string"?c:"\\u"+("0000"+a.charCodeAt(0!
).toString(16)).slice(-4)})+'"':'"'+string+'"'}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==="object"&&typeof value.toJSON==="function"){value=value.toJSON(key)}if(typeof rep==="function"){value=rep.call(holder,key,value)}switch(typeof value){case"string":return quote(value);case"number":return isFinite(value)?String(value):"null";case"boolean":case"null":return String(value);case"object":if(!value){return"null"}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==="[object Array]"){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||"null"}v=partial.length===0?"[]":gap?"[\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"]":"["+partial.join(",")+"]";gap=mind;return v}if(rep&&typeof rep==="object"){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==="string"){v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}else{for(k in value){if(Object.hasOwnProperty.call(value,k)){v=st!
r(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}v=partial.
length===0?"{}":gap?"{\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"}":"{"+partial.join(",")+"}";gap=mind;return v}}if(typeof JSON.stringify!=="function"){JSON.stringify=function(value,replacer,space){var i;gap="";indent="";if(typeof space==="number"){for(i=0;i<space;i+=1){indent+=" "}}else{if(typeof space==="string"){indent=space}}rep=replacer;if(replacer&&typeof replacer!=="function"&&(typeof replacer!=="object"||typeof replacer.length!=="number")){throw new Error("JSON.stringify")}return str("",{"":value})}}if(typeof JSON.parse!=="function"){JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==="object"){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v}else{delete value[k]}}}}return reviver.call(holder,key,value)}cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})}if(/^[\],:{}\s]*$/.test!
(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))){j=eval("("+text+")");return typeof reviver==="function"?walk({"":j},""):j}throw new SyntaxError("JSON.parse")}}}());
\ No newline at end of file
+if(!this.JSON){this.JSON={}}(function(){function f(n){return n<10?"0"+n:n}if(typeof Date.prototype.toJSON!=="function"){Date.prototype.toJSON=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf()}}var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==="string"?c:"\\u"+("0000"+a.ch!
arCodeAt(0).toString(16)).slice(-4)})+'"':'"'+string+'"'}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==="object"&&typeof value.toJSON==="function"){value=value.toJSON(key)}if(typeof rep==="function"){value=rep.call(holder,key,value)}switch(typeof value){case"string":return quote(value);case"number":return isFinite(value)?String(value):"null";case"boolean":case"null":return String(value);case"object":if(!value){return"null"}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==="[object Array]"){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||"null"}v=partial.length===0?"[]":gap?"[\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"]":"["+partial.join(",")+"]";gap=mind;return v}if(rep&&typeof rep==="object"){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==="string"){v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}else{for(k in value){if(Object.hasOwnProperty.call(valu!
e,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}
v=partial.length===0?"{}":gap?"{\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"}":"{"+partial.join(",")+"}";gap=mind;return v}}if(typeof JSON.stringify!=="function"){JSON.stringify=function(value,replacer,space){var i;gap="";indent="";if(typeof space==="number"){for(i=0;i<space;i+=1){indent+=" "}}else{if(typeof space==="string"){indent=space}}rep=replacer;if(replacer&&typeof replacer!=="function"&&(typeof replacer!=="object"||typeof replacer.length!=="number")){throw new Error("JSON.stringify")}return str("",{"":value})}}if(typeof JSON.parse!=="function"){JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==="object"){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v}else{delete value[k]}}}}return reviver.call(holder,key,value)}text=String(text);cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-!
4)})}if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))){j=eval("("+text+")");return typeof reviver==="function"?walk({"":j},""):j}throw new SyntaxError("JSON.parse")}}}());
\ No newline at end of file
diff -r a9402fc7ce1b -r 5536d57237a3 templates/display_base.mako
--- a/templates/display_base.mako Fri Apr 09 13:02:15 2010 -0400
+++ b/templates/display_base.mako Fri Apr 09 18:09:54 2010 -0400
@@ -32,29 +32,25 @@
<%def name="javascripts()">
${parent.javascripts()}
- ${h.js( "jquery", "jquery.tipsy", "galaxy.base", "json2", "jquery.autocomplete", "jquery.jstore-all", "autocomplete_tagging" )}
+ ${h.js( "jquery", "jquery.tipsy", "galaxy.base", "json2", "class", "jquery.jstore", "jquery.autocomplete", "autocomplete_tagging" )}
<script type="text/javascript">
- //
+
// Handle click on community tag.
- //
- function community_tag_click(tag_name, tag_value)
- {
+ function community_tag_click(tag_name, tag_value) {
<% controller_name = get_controller_name( item ) %>
var href = '${h.url_for ( controller='/' + controller_name , action='list_published')}';
href = href + "?f-tags=" + tag_name;
- if (tag_value != undefined && tag_value != "")
+ if (tag_value != undefined && tag_value != "") {
href = href + ":" + tag_value;
+ }
self.location = href;
}
- $(document).ready( function()
- {
+ $(function() {
// Set links to Galaxy screencasts to open in overlay.
- $(this).find("a[href^='http://screencast.g2.bx.psu.edu/']").each( function()
- {
- $(this).click( function()
- {
+ $(this).find("a[href^='http://screencast.g2.bx.psu.edu/']").each( function() {
+ $(this).click( function() {
var href = $(this).attr('href');
show_in_overlay(
{
@@ -67,7 +63,8 @@
return false;
});
});
-
+ // Init history boxes
+ init_history_items( $("div.historyItemWrapper") );
});
</script>
</%def>
@@ -76,14 +73,12 @@
${parent.stylesheets()}
${h.css( "autocomplete_tagging", "embed_item" )}
<style type="text/css">
- .page-body
- {
+ .page-body {
padding: 10px;
## float: left;
## width: 65%;
}
- .page-meta
- {
+ .page-meta {
float: right;
width: 27%;
padding: 0.5em;
diff -r a9402fc7ce1b -r 5536d57237a3 templates/history/display.mako
--- a/templates/history/display.mako Fri Apr 09 13:02:15 2010 -0400
+++ b/templates/history/display.mako Fri Apr 09 18:09:54 2010 -0400
@@ -1,207 +1,14 @@
<%inherit file="/display_base.mako"/>
<%namespace file="/root/history_common.mako" import="render_dataset" />
+## Set vars so that there's no need to change the code below.
+<%
+ history = published_item
+ datasets = published_item_data
+%>
+
<%def name="javascripts()">
${parent.javascripts()}
- ${h.js( "jquery.jstore-all" )}
-
- ## Set vars so that there's no need to change the code below.
- <%
- history = published_item
- datasets = published_item_data
- %>
-
- <script type="text/javascript">
- $(function() {
- // Load jStore for local storage
- $.extend(jQuery.jStore.defaults, { project: 'galaxy', flash: '/static/jStore.Flash.html' })
- $.jStore.load(); // Auto-select best storage
1
0
15 Apr '10
details: http://www.bx.psu.edu/hg/galaxy/rev/a9402fc7ce1b
changeset: 3625:a9402fc7ce1b
user: jeremy goecks <jeremy.goecks(a)emory.edu>
date: Fri Apr 09 13:02:15 2010 -0400
description:
Rollback text + autocomplete for big selects. Only use text + autocomplete for dbkey.
diffstat:
static/scripts/galaxy.base.js | 5 ++---
static/scripts/packed/galaxy.base.js | 2 +-
2 files changed, 3 insertions(+), 4 deletions(-)
diffs (24 lines):
diff -r 58ba6fbe22b3 -r a9402fc7ce1b static/scripts/galaxy.base.js
--- a/static/scripts/galaxy.base.js Fri Apr 09 11:35:02 2010 -0400
+++ b/static/scripts/galaxy.base.js Fri Apr 09 13:02:15 2010 -0400
@@ -120,10 +120,9 @@
}
// Replace any select box with 20+ options with a text input box + autocomplete.
-// TODO: make this work _well_ on pages with multiple forms; currently, value is reverted when any form is submitted -
-// value should be reverted when only the form that element is in is submitted.
+// TODO: make work with dynamic tool inputs and then can replace all big selects.
function replace_big_select_inputs() {
- $('select').each( function() {
+ $('select[name=dbkey]').each( function() {
var select_elt = $(this);
// Skip if there are < 20 options.
if (select_elt.find('option').length < 20)
diff -r 58ba6fbe22b3 -r a9402fc7ce1b static/scripts/packed/galaxy.base.js
--- a/static/scripts/packed/galaxy.base.js Fri Apr 09 11:35:02 2010 -0400
+++ b/static/scripts/packed/galaxy.base.js Fri Apr 09 13:02:15 2010 -0400
@@ -1,1 +1,1 @@
-$(document).ready(function(){replace_big_select_inputs()});$.fn.makeAbsolute=function(a){return this.each(function(){var b=$(this);var c=b.position();b.css({position:"absolute",marginLeft:0,marginTop:0,top:c.top,left:c.left,right:$(window).width()-(c.left+b.width())});if(a){b.remove().appendTo("body")}})};function ensure_popup_helper(){if($("#popup-helper").length===0){$("<div id='popup-helper'/>").css({background:"white",opacity:0,zIndex:15000,position:"absolute",top:0,left:0,width:"100%",height:"100%"}).appendTo("body").hide()}}function attach_popupmenu(b,d){var a=function(){d.unbind().hide();$("#popup-helper").unbind("click.popupmenu").hide()};var c=function(g){$("#popup-helper").bind("click.popupmenu",a).show();d.click(a).css({left:0,top:-1000}).show();var f=g.pageX-d.width()/2;f=Math.min(f,$(document).scrollLeft()+$(window).width()-$(d).width()-20);f=Math.max(f,$(document).scrollLeft()+20);d.css({top:g.pageY-5,left:f});return false};$(b).click(c)}function make_popupmen!
u(c,b){ensure_popup_helper();var a=$("<ul id='"+c.attr("id")+"-menu'></ul>");$.each(b,function(f,e){if(e){$("<li/>").html(f).click(e).appendTo(a)}else{$("<li class='head'/>").html(f).appendTo(a)}});var d=$("<div class='popmenu-wrapper'>");d.append(a).append("<div class='overlay-border'>").css("position","absolute").appendTo("body").hide();attach_popupmenu(c,d)}function make_popup_menus(){jQuery("div[popupmenu]").each(function(){var c={};$(this).find("a").each(function(){var b=$(this).attr("confirm"),d=$(this).attr("href"),e=$(this).attr("target");c[$(this).text()]=function(){if(!b||confirm(b)){var g=window;if(e=="_parent"){g=window.parent}else{if(e=="_top"){g=window.top}}g.location=d}}});var a=$("#"+$(this).attr("popupmenu"));make_popupmenu(a,c);$(this).remove();a.addClass("popup").show()})}function array_length(b){if(b.length){return b.length}var c=0;for(var a in b){c++}return c}function replace_big_select_inputs(){$("select").each(function(){var a=$(this);if(a.find("optio!
n").length<20){return}var b=a.attr("value");var c=$("<input type='text
' class='text-and-autocomplete-select'></input>");c.attr("size",40);c.attr("name",a.attr("name"));c.click(function(){var g=$(this).attr("value");$(this).attr("value","Loading...");$(this).showAllInCache();$(this).attr("value",g);$(this).select()});var f=[];var e={};a.children("option").each(function(){var h=$(this).text();var g=$(this).attr("value");if(g=="?"){return}f.push(h);e[h]=g;e[g]=g;if(g==b){c.attr("value",h)}});if(c.attr("value")==""){c.attr("value","Click to Search or Select")}var d={selectFirst:false,autoFill:false,mustMatch:false,matchContains:true,max:1000,minChars:0,hideForLessThanMinChars:false};c.autocomplete(f,d);a.replaceWith(c);c.parents("form").submit(function(){var h=c.attr("value");var g=e[h];if(g!==null&&g!==undefined){c.attr("value",g)}else{if(b!=""){c.attr("value",b)}else{c.attr("value","?")}}})})}function async_save_text(d,f,e,a,c,h,i,g,b){if(c===undefined){c=30}if(i===undefined){i=4}$("#"+d).live("click",function(){if($("#renaming-active").length>0!
){return}var l=$("#"+f),k=l.text(),j;if(h){j=$("<textarea></textarea>").attr({rows:i,cols:c}).text(k)}else{j=$("<input type='text'></input>").attr({value:k,size:c})}j.attr("id","renaming-active");j.blur(function(){$(this).remove();l.show();if(b){b(j)}});j.keyup(function(n){if(n.keyCode===27){$(this).trigger("blur")}else{if(n.keyCode===13){var m={};m[a]=$(this).val();$(this).trigger("blur");$.ajax({url:e,data:m,error:function(){alert("Text editing for elt "+f+" failed")},success:function(o){l.text(o);if(b){b(j)}}})}}});if(g){g(j)}l.hide();j.insertAfter(l);j.focus();j.select();return})}$(document).ready(function(){$("a[confirm]").click(function(){return confirm($(this).attr("confirm"))});if($.fn.tipsy){$(".tooltip").tipsy({gravity:"s"})}make_popup_menus()});
\ No newline at end of file
+$(document).ready(function(){replace_big_select_inputs()});$.fn.makeAbsolute=function(a){return this.each(function(){var b=$(this);var c=b.position();b.css({position:"absolute",marginLeft:0,marginTop:0,top:c.top,left:c.left,right:$(window).width()-(c.left+b.width())});if(a){b.remove().appendTo("body")}})};function ensure_popup_helper(){if($("#popup-helper").length===0){$("<div id='popup-helper'/>").css({background:"white",opacity:0,zIndex:15000,position:"absolute",top:0,left:0,width:"100%",height:"100%"}).appendTo("body").hide()}}function attach_popupmenu(b,d){var a=function(){d.unbind().hide();$("#popup-helper").unbind("click.popupmenu").hide()};var c=function(g){$("#popup-helper").bind("click.popupmenu",a).show();d.click(a).css({left:0,top:-1000}).show();var f=g.pageX-d.width()/2;f=Math.min(f,$(document).scrollLeft()+$(window).width()-$(d).width()-20);f=Math.max(f,$(document).scrollLeft()+20);d.css({top:g.pageY-5,left:f});return false};$(b).click(c)}function make_popupmen!
u(c,b){ensure_popup_helper();var a=$("<ul id='"+c.attr("id")+"-menu'></ul>");$.each(b,function(f,e){if(e){$("<li/>").html(f).click(e).appendTo(a)}else{$("<li class='head'/>").html(f).appendTo(a)}});var d=$("<div class='popmenu-wrapper'>");d.append(a).append("<div class='overlay-border'>").css("position","absolute").appendTo("body").hide();attach_popupmenu(c,d)}function make_popup_menus(){jQuery("div[popupmenu]").each(function(){var c={};$(this).find("a").each(function(){var b=$(this).attr("confirm"),d=$(this).attr("href"),e=$(this).attr("target");c[$(this).text()]=function(){if(!b||confirm(b)){var g=window;if(e=="_parent"){g=window.parent}else{if(e=="_top"){g=window.top}}g.location=d}}});var a=$("#"+$(this).attr("popupmenu"));make_popupmenu(a,c);$(this).remove();a.addClass("popup").show()})}function array_length(b){if(b.length){return b.length}var c=0;for(var a in b){c++}return c}function replace_big_select_inputs(){$("select[name=dbkey]").each(function(){var a=$(this);if(a!
.find("option").length<20){return}var b=a.attr("value");var c=$("<inpu
t type='text' class='text-and-autocomplete-select'></input>");c.attr("size",40);c.attr("name",a.attr("name"));c.click(function(){var g=$(this).attr("value");$(this).attr("value","Loading...");$(this).showAllInCache();$(this).attr("value",g);$(this).select()});var f=[];var e={};a.children("option").each(function(){var h=$(this).text();var g=$(this).attr("value");if(g=="?"){return}f.push(h);e[h]=g;e[g]=g;if(g==b){c.attr("value",h)}});if(c.attr("value")==""){c.attr("value","Click to Search or Select")}var d={selectFirst:false,autoFill:false,mustMatch:false,matchContains:true,max:1000,minChars:0,hideForLessThanMinChars:false};c.autocomplete(f,d);a.replaceWith(c);c.parents("form").submit(function(){var h=c.attr("value");var g=e[h];if(g!==null&&g!==undefined){c.attr("value",g)}else{if(b!=""){c.attr("value",b)}else{c.attr("value","?")}}})})}function async_save_text(d,f,e,a,c,h,i,g,b){if(c===undefined){c=30}if(i===undefined){i=4}$("#"+d).live("click",function(){if($("#renaming-activ!
e").length>0){return}var l=$("#"+f),k=l.text(),j;if(h){j=$("<textarea></textarea>").attr({rows:i,cols:c}).text(k)}else{j=$("<input type='text'></input>").attr({value:k,size:c})}j.attr("id","renaming-active");j.blur(function(){$(this).remove();l.show();if(b){b(j)}});j.keyup(function(n){if(n.keyCode===27){$(this).trigger("blur")}else{if(n.keyCode===13){var m={};m[a]=$(this).val();$(this).trigger("blur");$.ajax({url:e,data:m,error:function(){alert("Text editing for elt "+f+" failed")},success:function(o){l.text(o);if(b){b(j)}}})}}});if(g){g(j)}l.hide();j.insertAfter(l);j.focus();j.select();return})}$(document).ready(function(){$("a[confirm]").click(function(){return confirm($(this).attr("confirm"))});if($.fn.tipsy){$(".tooltip").tipsy({gravity:"s"})}make_popup_menus()});
\ No newline at end of file
1
0
15 Apr '10
details: http://www.bx.psu.edu/hg/galaxy/rev/58ba6fbe22b3
changeset: 3624:58ba6fbe22b3
user: Dan Blankenberg <dan(a)bx.psu.edu>
date: Fri Apr 09 11:35:02 2010 -0400
description:
Add additional tests to FASTQ Quality Trimmer tool
diffstat:
test-data/sanger_full_range_empty_reads.fastqsanger | 8 ++++++
tools/fastq/fastq_trimmer_by_quality.py | 11 ++++++--
tools/fastq/fastq_trimmer_by_quality.xml | 26 +++++++++++++++++++++
3 files changed, 42 insertions(+), 3 deletions(-)
diffs (90 lines):
diff -r ac044bb17d24 -r 58ba6fbe22b3 test-data/sanger_full_range_empty_reads.fastqsanger
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/sanger_full_range_empty_reads.fastqsanger Fri Apr 09 11:35:02 2010 -0400
@@ -0,0 +1,8 @@
+@FAKE0001 Original version has PHRED scores from 0 to 93 inclusive (in that order)
+
++
+
+@FAKE0002 Original version has PHRED scores from 93 to 0 inclusive (in that order)
+
++
+
diff -r ac044bb17d24 -r 58ba6fbe22b3 tools/fastq/fastq_trimmer_by_quality.py
--- a/tools/fastq/fastq_trimmer_by_quality.py Fri Apr 09 11:29:26 2010 -0400
+++ b/tools/fastq/fastq_trimmer_by_quality.py Fri Apr 09 11:35:02 2010 -0400
@@ -55,6 +55,12 @@
if len ( args ) != 2:
parser.error( "Need to specify an input file and an output file" )
+ if options.window_size < 1:
+ parser.error( 'You must specify a strictly positive window size' )
+
+ if options.window_step < 1:
+ parser.error( 'You must specify a strictly positive step size' )
+
#determine an exhaustive list of window indexes that can be excluded from aggregation
exclude_window_indexes = []
last_exclude_indexes = []
@@ -76,7 +82,6 @@
out = fastqWriter( open( args[1], 'wb' ), format = options.format )
action = ACTION_METHODS[ options.aggregation_action ]
- window_step = abs( options.window_step )
num_reads = None
num_reads_excluded = 0
@@ -93,7 +98,7 @@
if exclude_and_compare( action, quality_list[ lwindow_position:lwindow_position + options.window_size ], options.score_comparison, options.quality_score, exclude_window_indexes ):
fastq_read = fastq_read.slice( lwindow_position, None )
break
- lwindow_position += window_step
+ lwindow_position += options.window_step
else:
rwindow_position = len( quality_list ) #right position of window
while True:
@@ -105,7 +110,7 @@
if exclude_and_compare( action, quality_list[ lwindow_position:rwindow_position ], options.score_comparison, options.quality_score, exclude_window_indexes ):
fastq_read = fastq_read.slice( None, rwindow_position )
break
- rwindow_position -= window_step
+ rwindow_position -= options.window_step
if options.keep_zero_length or len( fastq_read ):
out.write( fastq_read )
else:
diff -r ac044bb17d24 -r 58ba6fbe22b3 tools/fastq/fastq_trimmer_by_quality.xml
--- a/tools/fastq/fastq_trimmer_by_quality.xml Fri Apr 09 11:29:26 2010 -0400
+++ b/tools/fastq/fastq_trimmer_by_quality.xml Fri Apr 09 11:35:02 2010 -0400
@@ -94,6 +94,32 @@
<param name="quality_score" value="1"/>
<output name="output_file" file="sanger_full_range_original_sanger.fastqsanger" />
</test>
+ <test>
+ <!-- Trim entire sequences; keep empty reads -->
+ <param name="input_file" value="sanger_full_range_original_sanger.fastqsanger" ftype="fastqsanger" />
+ <param name="keep_zero_length" value="true" />
+ <param name="trim_ends" value="53"/>
+ <param name="window_size" value="1"/>
+ <param name="step_size" value="1"/>
+ <param name="exclude_count" value="0"/>
+ <param name="aggregation_action" value="min"/>
+ <param name="score_comparison" value=">="/>
+ <param name="quality_score" value="999"/>
+ <output name="output_file" file="sanger_full_range_empty_reads.fastqsanger" />
+ </test>
+ <test>
+ <!-- Trim entire sequences; discard empty reads -->
+ <param name="input_file" value="sanger_full_range_original_sanger.fastqsanger" ftype="fastqsanger" />
+ <param name="keep_zero_length"/>
+ <param name="trim_ends" value="53"/>
+ <param name="window_size" value="1"/>
+ <param name="step_size" value="1"/>
+ <param name="exclude_count" value="0"/>
+ <param name="aggregation_action" value="min"/>
+ <param name="score_comparison" value=">="/>
+ <param name="quality_score" value="999"/>
+ <output name="output_file" file="empty_file.dat" />
+ </test>
</tests>
<help>
This tool allows you to trim the ends of reads based upon the aggregate value of quality scores found within a sliding window; a sliding window of size 1 is equivalent to 'simple' trimming of the ends.
1
0
15 Apr '10
details: http://www.bx.psu.edu/hg/galaxy/rev/ac044bb17d24
changeset: 3623:ac044bb17d24
user: Dan Blankenberg <dan(a)bx.psu.edu>
date: Fri Apr 09 11:29:26 2010 -0400
description:
Fix Test framework's handling of boolean and multiple select checkboxes and radio buttons.
diffstat:
test/base/twilltestcase.py | 22 +++++++---------------
1 files changed, 7 insertions(+), 15 deletions(-)
diffs (55 lines):
diff -r ab319f94495c -r ac044bb17d24 test/base/twilltestcase.py
--- a/test/base/twilltestcase.py Thu Apr 08 16:21:53 2010 -0400
+++ b/test/base/twilltestcase.py Fri Apr 09 11:29:26 2010 -0400
@@ -1000,24 +1000,21 @@
break
# To help with debugging a tool, print out the form controls when the test fails
print "form '%s' contains the following controls ( note the values )" % f.name
- control_names = []
- hidden_control_names = [] # cannot change these, so ignore or many complex page tool tests will fail
+ controls = {}
hc_prefix = '<HiddenControl('
for i, control in enumerate( f.controls ):
print "control %d: %s" % ( i, str( control ) )
- if hc_prefix in str(control):
- hidden_control_names.append(control.name) # cannot do much with these
- else:
+ if not hc_prefix in str( control ):
try:
#check if a repeat element needs to be added
if control.name not in kwd and control.name.endswith( '_add' ):
#control name doesn't exist, could be repeat
repeat_startswith = control.name[0:-4]
- if repeat_startswith and not [ c_name for c_name in control_names if c_name.startswith( repeat_startswith ) ] and [ c_name for c_name in kwd.keys() if c_name.startswith( repeat_startswith ) ]:
+ if repeat_startswith and not [ c_name for c_name in controls.keys() if c_name.startswith( repeat_startswith ) ] and [ c_name for c_name in kwd.keys() if c_name.startswith( repeat_startswith ) ]:
tc.submit( control.name )
return self.submit_form( form_no=form_no, button=button, **kwd )
# Check for refresh_on_change attribute, submit a change if required
- if 'refresh_on_change' in control.attrs.keys():
+ if hasattr( control, 'attrs' ) and 'refresh_on_change' in control.attrs.keys():
changed = False
item_labels = [ item.attrs[ 'label' ] for item in control.get_items() if item.selected ] #For DataToolParameter, control.value is the HDA id, but kwd contains the filename. This loop gets the filename/label for the selected values.
for value in kwd[ control.name ]:
@@ -1040,19 +1037,14 @@
except Exception, e:
log.debug( "In submit_form, continuing, but caught exception: %s" % str( e ) )
continue
- control_names.append( control.name )
+ controls[ control.name ] = control
# No refresh_on_change attribute found in current form, so process as usual
for control_name, control_value in kwd.items():
- if control_name in hidden_control_names:
+ if control_name not in controls:
continue # these cannot be handled safely - cause the test to barf out
if not isinstance( control_value, list ):
control_value = [ control_value ]
- try:
- control = f.find_control( name=control_name )
- except:
- # This assumes we always want the first control of the given name,
- # which may not be ideal...
- control = f.find_control( name=control_name, nr=0 )
+ control = controls[ control_name ]
control.clear()
if control.is_of_kind( "text" ):
tc.fv( f.name, control.name, ",".join( control_value ) )
1
0
details: http://www.bx.psu.edu/hg/galaxy/rev/ab319f94495c
changeset: 3622:ab319f94495c
user: rc
date: Thu Apr 08 16:21:53 2010 -0400
description:
forms: fixed a bug in default values
diffstat:
lib/galaxy/model/__init__.py | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diffs (12 lines):
diff -r 196b3b8bb6d8 -r ab319f94495c lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py Thu Apr 08 16:09:58 2010 -0400
+++ b/lib/galaxy/model/__init__.py Thu Apr 08 16:21:53 2010 -0400
@@ -1349,7 +1349,7 @@
value = False
else:
# Set other field types to the default value of the field
- value = field['default']
+ value = field.get('default', '')
# create the field widget
field_widget = eval( field[ 'type' ] )( field_name )
if field[ 'type' ] == 'TextField':
1
0
details: http://www.bx.psu.edu/hg/galaxy/rev/196b3b8bb6d8
changeset: 3621:196b3b8bb6d8
user: rc
date: Thu Apr 08 16:09:58 2010 -0400
description:
removed unused file.
diffstat:
scripts/galaxy_messaging/server/daemon.py | 34 -------------------------------
1 files changed, 0 insertions(+), 34 deletions(-)
diffs (38 lines):
diff -r 81de8629eeb6 -r 196b3b8bb6d8 scripts/galaxy_messaging/server/daemon.py
--- a/scripts/galaxy_messaging/server/daemon.py Thu Apr 08 15:25:38 2010 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-#!/usr/bin/env python
-"""
-
-Data Transfer Script Daemon
-
-This script is called from Galaxy LIMS once the lab admin starts the data
-transfer process using the user interface. This creates a child process and
-this child process starts the data_transfer.py script as a new process
-
-This script passes all the arguments from Galaxy LIMS to the data_transfer.py script
-
-Usage:
-
-python daemon.py <sequencer_host>
- <username>
- <password>
- <source_file>
- <sample_id>
- <dataset_index>
- <library_id>
- <folder_id>
-"""
-
-import sys, os
-# Perform first fork.
-try:
- pid = os.fork( )
- if pid > 0:
- sys.exit(0) # Exit first parent.
-except OSError, e:
- sys.stderr.write("fork #1 failed: (%d) %sn" % (e.errno, e.strerror))
- sys.exit(2)
-os.execv(os.path.join( os.getcwd(), "scripts/galaxy_messaging/server/data_transfer.py"), sys.argv)
-
1
0
15 Apr '10
details: http://www.bx.psu.edu/hg/galaxy/rev/81de8629eeb6
changeset: 3620:81de8629eeb6
user: Dan Blankenberg <dan(a)bx.psu.edu>
date: Thu Apr 08 15:25:38 2010 -0400
description:
Add FASTQ Quality Trimmer by sliding window tool.
diffstat:
test-data/sanger_full_range_quality_trimmed_out_1.fastqsanger | 8 +
test-data/sanger_full_range_quality_trimmed_out_2.fastqsanger | 8 +
test-data/sanger_full_range_quality_trimmed_out_3.fastqsanger | 8 +
tool_conf.xml.main | 1 +
tool_conf.xml.sample | 1 +
tools/fastq/fastq_trimmer_by_quality.py | 121 ++++++++++
tools/fastq/fastq_trimmer_by_quality.xml | 112 +++++++++
7 files changed, 259 insertions(+), 0 deletions(-)
diffs (299 lines):
diff -r 61b09dc1dff2 -r 81de8629eeb6 test-data/sanger_full_range_quality_trimmed_out_1.fastqsanger
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/sanger_full_range_quality_trimmed_out_1.fastqsanger Thu Apr 08 15:25:38 2010 -0400
@@ -0,0 +1,8 @@
+@FAKE0001 Original version has PHRED scores from 0 to 93 inclusive (in that order)
+ACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTAC
++
+56789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
+@FAKE0002 Original version has PHRED scores from 93 to 0 inclusive (in that order)
+CATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCA
++
+~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:98765
diff -r 61b09dc1dff2 -r 81de8629eeb6 test-data/sanger_full_range_quality_trimmed_out_2.fastqsanger
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/sanger_full_range_quality_trimmed_out_2.fastqsanger Thu Apr 08 15:25:38 2010 -0400
@@ -0,0 +1,8 @@
+@FAKE0001 Original version has PHRED scores from 0 to 93 inclusive (in that order)
+ACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTAC
++
+56789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
+@FAKE0002 Original version has PHRED scores from 93 to 0 inclusive (in that order)
+CATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCA
++
+~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!
diff -r 61b09dc1dff2 -r 81de8629eeb6 test-data/sanger_full_range_quality_trimmed_out_3.fastqsanger
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/sanger_full_range_quality_trimmed_out_3.fastqsanger Thu Apr 08 15:25:38 2010 -0400
@@ -0,0 +1,8 @@
+@FAKE0001 Original version has PHRED scores from 0 to 93 inclusive (in that order)
+ACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTAC
++
+!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
+@FAKE0002 Original version has PHRED scores from 93 to 0 inclusive (in that order)
+CATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCATGCA
++
+~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:98765
diff -r 61b09dc1dff2 -r 81de8629eeb6 tool_conf.xml.main
--- a/tool_conf.xml.main Wed Apr 07 11:12:00 2010 -0400
+++ b/tool_conf.xml.main Thu Apr 08 15:25:38 2010 -0400
@@ -295,6 +295,7 @@
<label text="Generic FASTQ manipulation" id="generic_fastq" />
<tool file="fastq/fastq_filter.xml" />
<tool file="fastq/fastq_trimmer.xml" />
+ <tool file="fastq/fastq_trimmer_by_quality.xml" />
<tool file="fastq/fastq_manipulation.xml" />
<tool file="fastq/fastq_to_fasta.xml" />
<tool file="fastq/fastq_to_tabular.xml" />
diff -r 61b09dc1dff2 -r 81de8629eeb6 tool_conf.xml.sample
--- a/tool_conf.xml.sample Wed Apr 07 11:12:00 2010 -0400
+++ b/tool_conf.xml.sample Thu Apr 08 15:25:38 2010 -0400
@@ -209,6 +209,7 @@
<label text="Generic FASTQ manipulation" id="generic_fastq" />
<tool file="fastq/fastq_filter.xml" />
<tool file="fastq/fastq_trimmer.xml" />
+ <tool file="fastq/fastq_trimmer_by_quality.xml" />
<tool file="fastq/fastq_manipulation.xml" />
<tool file="fastq/fastq_to_fasta.xml" />
<tool file="fastq/fastq_to_tabular.xml" />
diff -r 61b09dc1dff2 -r 81de8629eeb6 tools/fastq/fastq_trimmer_by_quality.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/fastq/fastq_trimmer_by_quality.py Thu Apr 08 15:25:38 2010 -0400
@@ -0,0 +1,121 @@
+#Dan Blankenberg
+from optparse import OptionParser
+from galaxy_utils.sequence.fastq import fastqReader, fastqWriter
+
+def mean( score_list ):
+ return float( sum( score_list ) ) / float( len( score_list ) )
+
+ACTION_METHODS = { 'min':min, 'max':max, 'sum':sum, 'mean':mean }
+
+def compare( aggregated_value, operator, threshold_value ):
+ if operator == '>':
+ return aggregated_value > threshold_value
+ elif operator == '>=':
+ return aggregated_value >= threshold_value
+ elif operator == '==':
+ return aggregated_value == threshold_value
+ elif operator == '<':
+ return aggregated_value < threshold_value
+ elif operator == '<=':
+ return aggregated_value <= threshold_value
+ elif operator == '!=':
+ return aggregated_value != threshold_value
+
+def exclude( value_list, exclude_indexes ):
+ rval = []
+ for i, val in enumerate( value_list ):
+ if i not in exclude_indexes:
+ rval.append( val )
+ return rval
+
+def exclude_and_compare( aggregate_action, aggregate_list, operator, threshold_value, exclude_indexes = None ):
+ if not aggregate_list or compare( aggregate_action( aggregate_list ), operator, threshold_value ):
+ return True
+ if exclude_indexes:
+ for exclude_index in exclude_indexes:
+ excluded_list = exclude( aggregate_list, exclude_index )
+ if not excluded_list or compare( aggregate_action( excluded_list ), operator, threshold_value ):
+ return True
+ return False
+
+def main():
+ usage = "usage: %prog [options] input_file output_file"
+ parser = OptionParser( usage=usage )
+ parser.add_option( '-f', '--format', dest='format', type='choice', default='sanger', choices=( 'sanger', 'cssanger', 'solexa', 'illumina' ), help='FASTQ variant type' )
+ parser.add_option( '-s', '--window_size', type="int", dest='window_size', default='1', help='Window size' )
+ parser.add_option( '-t', '--window_step', type="int", dest='window_step', default='1', help='Window step' )
+ parser.add_option( '-e', '--trim_ends', type="choice", dest='trim_ends', default='53', choices=('5','3','53','35' ), help='Ends to Trim' )
+ parser.add_option( '-a', '--aggregation_action', type="choice", dest='aggregation_action', default='min', choices=('min','max','sum','mean' ), help='Aggregate action for window' )
+ parser.add_option( '-x', '--exclude_count', type="int", dest='exclude_count', default='0', help='Maximum number of bases to exclude from the window during aggregation' )
+ parser.add_option( '-c', '--score_comparison', type="choice", dest='score_comparison', default='>=', choices=('>','>=','==','<', '<=', '!=' ), help='Keep read when aggregate score is' )
+ parser.add_option( '-q', '--quality_score', type="float", dest='quality_score', default='0', help='Quality Score' )
+ parser.add_option( "-k", "--keep_zero_length", action="store_true", dest="keep_zero_length", default=False, help="Keep reads with zero length")
+ ( options, args ) = parser.parse_args()
+
+ if len ( args ) != 2:
+ parser.error( "Need to specify an input file and an output file" )
+
+ #determine an exhaustive list of window indexes that can be excluded from aggregation
+ exclude_window_indexes = []
+ last_exclude_indexes = []
+ for exclude_count in range( min( options.exclude_count, options.window_size ) ):
+ if last_exclude_indexes:
+ new_exclude_indexes = []
+ for exclude_list in last_exclude_indexes:
+ for window_index in range( options.window_size ):
+ if window_index not in exclude_list:
+ new_exclude = sorted( exclude_list + [ window_index ] )
+ if new_exclude not in exclude_window_indexes + new_exclude_indexes:
+ new_exclude_indexes.append( new_exclude )
+ exclude_window_indexes += new_exclude_indexes
+ last_exclude_indexes = new_exclude_indexes
+ else:
+ for window_index in range( options.window_size ):
+ last_exclude_indexes.append( [ window_index ] )
+ exclude_window_indexes = list( last_exclude_indexes )
+
+ out = fastqWriter( open( args[1], 'wb' ), format = options.format )
+ action = ACTION_METHODS[ options.aggregation_action ]
+ window_step = abs( options.window_step )
+
+ num_reads = None
+ num_reads_excluded = 0
+ for num_reads, fastq_read in enumerate( fastqReader( open( args[0] ), format = options.format ) ):
+ for trim_end in options.trim_ends:
+ quality_list = fastq_read.get_decimal_quality_scores()
+ if trim_end == '5':
+ lwindow_position = 0 #left position of window
+ while True:
+ if lwindow_position >= len( quality_list ):
+ fastq_read.sequence = ''
+ fastq_read.quality = ''
+ break
+ if exclude_and_compare( action, quality_list[ lwindow_position:lwindow_position + options.window_size ], options.score_comparison, options.quality_score, exclude_window_indexes ):
+ fastq_read = fastq_read.slice( lwindow_position, None )
+ break
+ lwindow_position += window_step
+ else:
+ rwindow_position = len( quality_list ) #right position of window
+ while True:
+ lwindow_position = rwindow_position - options.window_size #left position of window
+ if rwindow_position <= 0 or lwindow_position < 0:
+ fastq_read.sequence = ''
+ fastq_read.quality = ''
+ break
+ if exclude_and_compare( action, quality_list[ lwindow_position:rwindow_position ], options.score_comparison, options.quality_score, exclude_window_indexes ):
+ fastq_read = fastq_read.slice( None, rwindow_position )
+ break
+ rwindow_position -= window_step
+ if options.keep_zero_length or len( fastq_read ):
+ out.write( fastq_read )
+ else:
+ num_reads_excluded += 1
+ out.close()
+ if num_reads is None:
+ print "No valid FASTQ reads could be processed."
+ else:
+ print "%i FASTQ reads were processed." % ( num_reads + 1 )
+ if num_reads_excluded:
+ print "%i reads of zero length were excluded from the output." % num_reads_excluded
+
+if __name__ == "__main__": main()
diff -r 61b09dc1dff2 -r 81de8629eeb6 tools/fastq/fastq_trimmer_by_quality.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/fastq/fastq_trimmer_by_quality.xml Thu Apr 08 15:25:38 2010 -0400
@@ -0,0 +1,112 @@
+<tool id="fastq_quality_trimmer" name="FASTQ Quality Trimmer" version="1.0.0">
+ <description>by sliding window</description>
+ <command interpreter="python">fastq_trimmer_by_quality.py '$input_file' '$output_file' -f '${input_file.extension[len( 'fastq' ):]}' -s '$window_size'
+ -t '$step_size' -e '$trim_ends' -a '$aggregation_action' -x '$exclude_count' -c '$score_comparison' -q '$quality_score'
+ #if $keep_zero_length.value:
+ -k
+ #end if
+ </command>
+ <inputs>
+ <param name="input_file" type="data" format="fastqsanger,fastqcssanger" label="FASTQ File"/>
+ <param name="keep_zero_length" label="Keep reads with zero length" type="boolean" truevalue="keep_zero_length" falsevalue="exclude_zero_length" selected="False"/>
+ <param name="trim_ends" type="select" label="Trim ends">
+ <option value="53" selected="True">5' and 3'</option>
+ <option value="5">5' only</option>
+ <option value="3">3' only</option>
+ </param>
+ <param name="window_size" type="integer" value="1" label="Window size"/>
+ <param name="step_size" type="integer" value="1" label="Step Size" />
+ <param name="exclude_count" label="Maximum number of bases to exclude from the window during aggregation" value="0" type="integer" />
+ <param name="aggregation_action" type="select" label="Aggregate action for window">
+ <option value="min" selected="True">min score</option>
+ <option value="max">max score</option>
+ <option value="sum">sum of scores</option>
+ <option value="mean">mean of scores</option>
+ </param>
+ <param name="score_comparison" type="select" label="Trim until aggregate score is">
+ <sanitizer>
+ <valid initial="none">
+ <add value="<>=!"/> <!-- only allow lt, gt, e, le, ge, ne for this parameter; will be single-quote escaped on commandline -->
+ </valid>
+ </sanitizer>
+ <option value=">">></option>
+ <option value=">=" selected="true">>=</option>
+ <option value="==">==</option>
+ <option value="!=">!=</option>
+ <option value="<"><</option>
+ <option value="<="><=</option>
+ </param>
+ <param name="quality_score" label="Quality Score" value="0" type="float" />
+ </inputs>
+ <outputs>
+ <data name="output_file" format="input" />
+ </outputs>
+ <tests>
+ <test>
+ <!-- Trim until window size 1 >= 20;both ends -->
+ <param name="input_file" value="sanger_full_range_original_sanger.fastqsanger" ftype="fastqsanger" />
+ <param name="keep_zero_length" value="exclude_zero_length" />
+ <param name="trim_ends" value="53"/>
+ <param name="window_size" value="1"/>
+ <param name="step_size" value="1"/>
+ <param name="exclude_count" value="0"/>
+ <param name="aggregation_action" value="min"/>
+ <param name="score_comparison" value=">="/>
+ <param name="quality_score" value="20"/>
+ <output name="output_file" file="sanger_full_range_quality_trimmed_out_1.fastqsanger" />
+ </test>
+ <test>
+ <!-- Trim until window size 1 >= 20; 5' end only -->
+ <param name="input_file" value="sanger_full_range_original_sanger.fastqsanger" ftype="fastqsanger" />
+ <param name="keep_zero_length" value="exclude_zero_length" />
+ <param name="trim_ends" value="5"/>
+ <param name="window_size" value="1"/>
+ <param name="step_size" value="1"/>
+ <param name="exclude_count" value="0"/>
+ <param name="aggregation_action" value="min"/>
+ <param name="score_comparison" value=">="/>
+ <param name="quality_score" value="20"/>
+ <output name="output_file" file="sanger_full_range_quality_trimmed_out_2.fastqsanger" />
+ </test>
+ <test>
+ <!-- Trim until window size 1 >= 20; 3' end only -->
+ <param name="input_file" value="sanger_full_range_original_sanger.fastqsanger" ftype="fastqsanger" />
+ <param name="keep_zero_length" value="exclude_zero_length" />
+ <param name="trim_ends" value="3"/>
+ <param name="window_size" value="1"/>
+ <param name="step_size" value="1"/>
+ <param name="exclude_count" value="0"/>
+ <param name="aggregation_action" value="min"/>
+ <param name="score_comparison" value=">="/>
+ <param name="quality_score" value="20"/>
+ <output name="output_file" file="sanger_full_range_quality_trimmed_out_3.fastqsanger" />
+ </test>
+ <test>
+ <!-- Trim until window size 2 >= 1;both ends, 1 deviant score -->
+ <param name="input_file" value="sanger_full_range_original_sanger.fastqsanger" ftype="fastqsanger" />
+ <param name="keep_zero_length" value="exclude_zero_length" />
+ <param name="trim_ends" value="53"/>
+ <param name="window_size" value="2"/>
+ <param name="step_size" value="1"/>
+ <param name="exclude_count" value="1"/>
+ <param name="aggregation_action" value="min"/>
+ <param name="score_comparison" value=">="/>
+ <param name="quality_score" value="1"/>
+ <output name="output_file" file="sanger_full_range_original_sanger.fastqsanger" />
+ </test>
+ </tests>
+ <help>
+This tool allows you to trim the ends of reads based upon the aggregate value of quality scores found within a sliding window; a sliding window of size 1 is equivalent to 'simple' trimming of the ends.
+
+The user specifies the aggregating action (min, max, sum, mean) to perform on the quality score values found within the sliding window to be used with the user defined comparison operation and comparison value.
+
+The user can provide a maximum count of bases that can be excluded from the aggregation within the window. When set, this tool will first check the aggregation of the entire window, then after removing 1 value, then after removing 2 values, up to the number declared. Setting this value to be equal to or greater than the window size will cause no trimming to occur.
+
+-----
+
+.. class:: warningmark
+
+Trimming a color space read will cause any adapter base to be lost.
+
+ </help>
+</tool>
1
0
details: http://www.bx.psu.edu/hg/galaxy/rev/61b09dc1dff2
changeset: 3619:61b09dc1dff2
user: rc
date: Wed Apr 07 11:12:00 2010 -0400
description:
sff converter tool
diffstat:
tool_conf.xml.sample | 1 +
tools/filters/sff_extract.py | 1505 +++++++++++++++++++++++++++++++++++++++
tools/filters/sff_extractor.xml | 22 +
3 files changed, 1528 insertions(+), 0 deletions(-)
diffs (1546 lines):
diff -r ebfc9236bf5a -r 61b09dc1dff2 tool_conf.xml.sample
--- a/tool_conf.xml.sample Wed Apr 07 08:44:57 2010 -0400
+++ b/tool_conf.xml.sample Wed Apr 07 11:12:00 2010 -0400
@@ -78,6 +78,7 @@
<tool file="fasta_tools/tabular_to_fasta.xml" />
<tool file="fastx_toolkit/fastq_to_fasta.xml" />
<tool file="filters/wiggle_to_simple.xml" />
+ <tool file="filters/sff_extractor.xml" />
</section>
<section name="Extract Features" id="features">
<tool file="filters/ucsc_gene_bed_to_exon_bed.xml" />
diff -r ebfc9236bf5a -r 61b09dc1dff2 tools/filters/sff_extract.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/filters/sff_extract.py Wed Apr 07 11:12:00 2010 -0400
@@ -0,0 +1,1505 @@
+#!/usr/bin/python
+'''This software extracts the seq, qual and ancillary information from an sff
+file, like the ones used by the 454 sequencer.
+
+Optionally, it can also split paired-end reads if given the linker sequence.
+The splitting is done with maximum match, i.e., every occurence of the linker
+sequence will be removed, even if occuring multiple times.'''
+
+#copyright Jose Blanca and Bastien Chevreux
+#COMAV institute, Universidad Politecnica de Valencia (UPV)
+#Valencia, Spain
+
+# additions to handle paired end reads by Bastien Chevreux
+# bugfixes for linker specific lengths: Lionel Guy
+
+#This program is free software: you can redistribute it and/or modify
+#it under the terms of the GNU General Public License as published by
+#the Free Software Foundation, either version 3 of the License, or
+#(at your option) any later version.
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+#You should have received a copy of the GNU General Public License
+#along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+__author__ = 'Jose Blanca and Bastien Chevreux'
+__copyright__ = 'Copyright 2008, Jose Blanca, COMAV, and Bastien Chevreux'
+__license__ = 'GPLv3 or later'
+__version__ = '0.2.8'
+__email__ = 'jblanca(a)btc.upv.es'
+__status__ = 'beta'
+
+import struct
+import sys
+import os
+import subprocess
+import tempfile
+
+
+fake_sff_name = 'fake_sff_name'
+
+
+# readname as key: lines with matches from SSAHA, one best match
+ssahapematches = {}
+# linker readname as key: length of linker sequence
+linkerlengths = {}
+
+# set to true if something really fishy is going on with the sequences
+stern_warning = True
+
+def read_bin_fragment(struct_def, fileh, offset=0, data=None,
+ byte_padding=None):
+ '''It reads a chunk of a binary file.
+
+ You have to provide the struct, a file object, the offset (where to start
+ reading).
+ Also you can provide an optional dict that will be populated with the
+ extracted data.
+ If a byte_padding is given the number of bytes read will be a multiple of
+ that number, adding the required pad at the end.
+ It returns the number of bytes reads and the data dict.
+ '''
+ if data is None:
+ data = {}
+
+ #we read each item
+ bytes_read = 0
+ for item in struct_def:
+ #we go to the place and read
+ fileh.seek(offset + bytes_read)
+ n_bytes = struct.calcsize(item[1])
+ buffer = fileh.read(n_bytes)
+ read = struct.unpack('>' + item[1], buffer)
+ if len(read) == 1:
+ read = read[0]
+ data[item[0]] = read
+ bytes_read += n_bytes
+
+ #if there is byte_padding the bytes_to_read should be a multiple of the
+ #byte_padding
+ if byte_padding is not None:
+ pad = byte_padding
+ bytes_read = ((bytes_read + pad - 1) // pad) * pad
+
+ return (bytes_read, data)
+
+
+def check_magic(magic):
+ '''It checks that the magic number of the file matches the sff magic.'''
+ if magic != 779314790:
+ raise RuntimeError('This file does not seems to be an sff file.')
+
+def check_version(version):
+ '''It checks that the version is supported, otherwise it raises an error.'''
+ supported = ('\x00', '\x00', '\x00', '\x01')
+ i = 0
+ for item in version:
+ if version[i] != supported[i]:
+ raise RuntimeError('SFF version not supported. Please contact the author of the software.')
+ i += 1
+
+def read_header(fileh):
+ '''It reads the header from the sff file and returns a dict with the
+ information'''
+ #first we read the first part of the header
+ head_struct = [
+ ('magic_number', 'I'),
+ ('version', 'cccc'),
+ ('index_offset', 'Q'),
+ ('index_length', 'I'),
+ ('number_of_reads', 'I'),
+ ('header_length', 'H'),
+ ('key_length', 'H'),
+ ('number_of_flows_per_read', 'H'),
+ ('flowgram_format_code', 'B'),
+ ]
+ data = {}
+ first_bytes, data = read_bin_fragment(struct_def=head_struct, fileh=fileh,
+ offset=0, data=data)
+ check_magic(data['magic_number'])
+ check_version(data['version'])
+ #now that we know the number_of_flows_per_read and the key_length
+ #we can read the second part of the header
+ struct2 = [
+ ('flow_chars', str(data['number_of_flows_per_read']) + 'c'),
+ ('key_sequence', str(data['key_length']) + 'c')
+ ]
+ read_bin_fragment(struct_def=struct2, fileh=fileh, offset=first_bytes,
+ data=data)
+ return data
+
+
+def read_sequence(header, fileh, fposition):
+ '''It reads one read from the sff file located at the fposition and
+ returns a dict with the information.'''
+ header_length = header['header_length']
+ index_offset = header['index_offset']
+ index_length = header['index_length']
+
+ #the sequence struct
+ read_header_1 = [
+ ('read_header_length', 'H'),
+ ('name_length', 'H'),
+ ('number_of_bases', 'I'),
+ ('clip_qual_left', 'H'),
+ ('clip_qual_right', 'H'),
+ ('clip_adapter_left', 'H'),
+ ('clip_adapter_right', 'H'),
+ ]
+ def read_header_2(name_length):
+ '''It returns the struct definition for the second part of the header'''
+ return [('name', str(name_length) +'c')]
+ def read_data(number_of_bases):
+ '''It returns the struct definition for the read data section.'''
+ #size = {'c': 1, 'B':1, 'H':2, 'I':4, 'Q':8}
+ if header['flowgram_format_code'] == 1:
+ flow_type = 'H'
+ else:
+ raise Error('file version not supported')
+ number_of_bases = str(number_of_bases)
+ return [
+ ('flowgram_values', str(header['number_of_flows_per_read']) +
+ flow_type),
+ ('flow_index_per_base', number_of_bases + 'B'),
+ ('bases', number_of_bases + 'c'),
+ ('quality_scores', number_of_bases + 'B'),
+ ]
+
+ data = {}
+ #we read the first part of the header
+ bytes_read, data = read_bin_fragment(struct_def=read_header_1,
+ fileh=fileh, offset=fposition, data=data)
+
+ read_bin_fragment(struct_def=read_header_2(data['name_length']),
+ fileh=fileh, offset=fposition + bytes_read, data=data)
+ #we join the letters of the name
+ data['name'] = ''.join(data['name'])
+ offset = data['read_header_length']
+ #we read the sequence and the quality
+ read_data_st = read_data(data['number_of_bases'])
+ bytes_read, data = read_bin_fragment(struct_def=read_data_st,
+ fileh=fileh, offset=fposition + offset,
+ data=data, byte_padding=8)
+ #we join the bases
+ data['bases'] = ''.join(data['bases'])
+
+ #print data
+ #print "pre cqr: ", data['clip_qual_right']
+ #print "pre car: ", data['clip_adapter_right']
+ #print "pre cql: ", data['clip_qual_left']
+ #print "pre cal: ", data['clip_adapter_left']
+
+ # correct for the case the right clip is <= than the left clip
+ # in this case, left clip is 0 are set to 0 (right clip == 0 means
+ # "whole sequence")
+ if data['clip_qual_right'] <= data['clip_qual_left'] :
+ data['clip_qual_right'] = 0
+ data['clip_qual_left'] = 0
+ if data['clip_adapter_right'] <= data['clip_adapter_left'] :
+ data['clip_adapter_right'] = 0
+ data['clip_adapter_left'] = 0
+
+ #the clipping section follows the NCBI's guidelines Trace Archive RFC
+ #http://www.ncbi.nlm.nih.gov/Traces/trace.cgi?cmd=show&f=rfc&m=doc&s=rfc
+ #if there's no adapter clip: qual -> vector
+ #else: qual-> qual
+ # adapter -> vector
+
+ if not data['clip_adapter_left']:
+ data['clip_adapter_left'], data['clip_qual_left'] = data['clip_qual_left'], data['clip_adapter_left']
+ if not data['clip_adapter_right']:
+ data['clip_adapter_right'], data['clip_qual_right'] = data['clip_qual_right'], data['clip_adapter_right']
+
+ # see whether we have to override the minimum left clips
+ if config['min_leftclip'] > 0:
+ if data['clip_adapter_left'] >0 and data['clip_adapter_left'] < config['min_leftclip']:
+ data['clip_adapter_left'] = config['min_leftclip']
+ if data['clip_qual_left'] >0 and data['clip_qual_left'] < config['min_leftclip']:
+ data['clip_qual_left'] = config['min_leftclip']
+
+
+ #print "post cqr: ", data['clip_qual_right']
+ #print "post car: ", data['clip_adapter_right']
+ #print "post cql: ", data['clip_qual_left']
+ #print "post cal: ", data['clip_adapter_left']
+
+
+ # for handling the -c (clip) option gently, we already clip here
+ # and set all clip points to the sequence end points
+ if config['clip']:
+ data['bases'], data['quality_scores'] = clip_read(data)
+
+ data['number_of_bases']=len(data['bases'])
+ data['clip_qual_right'] = data['number_of_bases']
+ data['clip_adapter_right'] = data['number_of_bases']
+ data['clip_qual_left'] = 0
+ data['clip_adapter_left'] = 0
+
+ return data['read_header_length'] + bytes_read, data
+
+
+def sequences(fileh, header):
+ '''It returns a generator with the data for each read.'''
+ #now we can read all the sequences
+ fposition = header['header_length'] #position in the file
+ reads_read = 0
+ while True:
+ if fposition == header['index_offset']:
+ #we have to skip the index section
+ fposition += index_length
+ continue
+ else:
+ bytes_read, seq_data = read_sequence(header=header, fileh=fileh,
+ fposition=fposition)
+ yield seq_data
+ fposition += bytes_read
+ reads_read += 1
+ if reads_read >= header['number_of_reads']:
+ break
+
+
+def remove_last_xmltag_in_file(fname, tag=None):
+ '''Given an xml file name and a tag, it removes the last tag of the
+ file if it matches the given tag. Tag removal is performed via file
+ truncation.
+
+ It the given tag is not the last in the file, a RunTimeError will be
+ raised.
+
+ The resulting xml file will be not xml valid. This function is a hack
+ that allows to append records to xml files in a quick and dirty way.
+ '''
+
+ fh = open(fname, 'r+')
+ #we have to read from the end to the start of the file and keep the
+ #string enclosed by </ >
+ i = -1
+ last_tag = [] #the chars that form the last tag
+ start_offset = None #in which byte does the last tag starts?
+ end_offset = None #in which byte does the last tag ends?
+ while True:
+ fh.seek(i, 2)
+ char = fh.read(1)
+ if not char.isspace():
+ last_tag.append(char)
+ if char == '>':
+ end_offset = i
+ if char == '<':
+ start_offset = i
+ break
+ i -= 1
+
+ #we have read the last tag backwards
+ last_tag = ''.join(last_tag[::-1])
+ #we remove the </ and >
+ last_tag = last_tag.rstrip('>').lstrip('</')
+
+ #we check that we're removing the asked tag
+ if tag is not None and tag != last_tag:
+ raise RuntimeError("The given xml tag wasn't the last one in the file")
+
+ # while we are at it: also remove all white spaces in that line :-)
+ i -= 1
+ while True:
+ fh.seek(i, 2)
+ char = fh.read(1)
+ if not char == ' ' and not char == '\t':
+ break;
+ if fh.tell() == 1:
+ break;
+ i -= 1
+
+ fh.truncate();
+
+ fh.close()
+ return last_tag
+
+
+def create_basic_xml_info(readname, fname):
+ '''Formats a number of read specific infos into XML format.
+ Currently formated: name and the tags set from command line
+ '''
+ to_print = [' <trace>\n']
+ to_print.append(' <trace_name>')
+ to_print.append(readname)
+ to_print.append('</trace_name>\n')
+
+ #extra information
+ #do we have extra info for this file?
+ info = None
+ if config['xml_info']:
+ #with this name?
+ if fname in config['xml_info']:
+ info = config['xml_info'][fname]
+ else:
+ #with no name?
+ try:
+ info = config['xml_info'][fake_sff_name]
+ except KeyError:
+ pass
+ #we print the info that we have
+ if info:
+ for key in info:
+ to_print.append(' <' + key + '>' + info[key] + \
+ '</' + key +'>\n')
+
+ return ''.join(to_print)
+
+
+def create_clip_xml_info(readlen, adapl, adapr, quall, qualr):
+ '''Takes the clip values of the read and formats them into XML
+ Corrects "wrong" values that might have resulted through
+ simplified calculations earlier in the process of conversion
+ (especially during splitting of paired-end reads)
+ '''
+
+ to_print = [""]
+
+ # if right borders are >= to read length, they don't need
+ # to be printed
+ if adapr >= readlen:
+ adapr = 0
+ if qualr >= readlen:
+ qualr = 0
+
+ # BaCh
+ # when called via split_paired_end(), some values may be < 0
+ # (when clip values were 0 previously)
+ # instead of putting tons of if clauses for different calculations there,
+ # I centralise corrective measure here
+ # set all values <0 to 0
+
+ if adapr < 0:
+ adapr = 0
+ if qualr <0:
+ qualr = 0
+ if adapl < 0:
+ adapl = 0
+ if quall <0:
+ quall = 0
+
+ if quall:
+ to_print.append(' <clip_quality_left>')
+ to_print.append(str(quall))
+ to_print.append('</clip_quality_left>\n')
+ if qualr:
+ to_print.append(' <clip_quality_right>')
+ to_print.append(str(qualr))
+ to_print.append('</clip_quality_right>\n')
+ if adapl:
+ to_print.append(' <clip_vector_left>')
+ to_print.append(str(adapl))
+ to_print.append('</clip_vector_left>\n')
+ if adapr:
+ to_print.append(' <clip_vector_right>')
+ to_print.append(str(adapr))
+ to_print.append('</clip_vector_right>\n')
+ return ''.join(to_print)
+
+
+def create_xml_for_unpaired_read(data, fname):
+ '''Given the data for one read it returns an str with the xml ancillary
+ data.'''
+ to_print = [create_basic_xml_info(data['name'],fname)]
+ #clippings in the XML only if we do not hard clip
+ if not config['clip']:
+ to_print.append(create_clip_xml_info(data['number_of_bases'],data['clip_adapter_left'], data['clip_adapter_right'], data['clip_qual_left'], data['clip_qual_right']));
+ to_print.append(' </trace>\n')
+ return ''.join(to_print)
+
+
+def format_as_fasta(name,seq,qual):
+ name_line = ''.join(('>', name,'\n'))
+ seqstring = ''.join((name_line, seq, '\n'))
+ qual_line = ' '.join([str(q) for q in qual])
+ qualstring = ''.join((name_line, qual_line, '\n'))
+ return seqstring, qualstring
+
+def format_as_fastq(name,seq,qual):
+ qual_line = ''.join([chr(q+33) for q in qual])
+ #seqstring = ''.join(('@', name,'\n', seq, '\n+', name,'\n', qual_line, '\n'))
+ seqstring = ''.join(('@', name,'\n', seq, '\n+\n', qual_line, '\n'))
+ return seqstring
+
+
+def get_read_data(data):
+ '''Given the data for one read it returns 2 strs with the fasta seq
+ and fasta qual.'''
+ #seq and qual
+ if config['mix_case']:
+ seq = sequence_case(data)
+ qual = data['quality_scores']
+ else :
+ seq = data['bases']
+ qual = data['quality_scores']
+
+ return seq, qual
+
+def extract_read_info(data, fname):
+ '''Given the data for one read it returns 3 strs with the fasta seq, fasta
+ qual and xml ancillary data.'''
+
+ seq,qual = get_read_data(data)
+ seqstring, qualstring = format_as_fasta(data['name'],seq,qual)
+
+ #name_line = ''.join(('>', data['name'],'\n'))
+ #seq = ''.join((name_line, seq, '\n'))
+ #qual_line = ' '.join([str(q) for q in qual])
+ #qual = ''.join((name_line, qual_line, '\n'))
+
+ xmlstring = create_xml_for_unpaired_read(data, fname)
+
+ return seqstring, qualstring, xmlstring
+
+def write_sequence(name,seq,qual,seq_fh,qual_fh):
+ '''Write sequence and quality FASTA and FASTA qual filehandles
+ (or into FASTQ and XML)
+ if sequence length is 0, don't write'''
+
+ if len(seq) == 0 : return
+
+ if qual_fh is None:
+ seq_fh.write(format_as_fastq(name,seq,qual))
+ else:
+ seqstring, qualstring = format_as_fasta(name,seq,qual)
+ seq_fh.write(seqstring)
+ qual_fh.write(qualstring)
+ return
+
+def write_unpaired_read(data, sff_fh, seq_fh, qual_fh, xml_fh):
+ '''Writes an unpaired read into FASTA, FASTA qual and XML filehandles
+ (or into FASTQ and XML)
+ if sequence length is 0, don't write'''
+
+ seq,qual = get_read_data(data)
+ if len(seq) == 0 : return
+
+ write_sequence(data['name'],seq,qual,seq_fh,qual_fh)
+
+ anci = create_xml_for_unpaired_read(data, sff_fh.name)
+ if anci is not None:
+ xml_fh.write(anci)
+ return
+
+
+def reverse_complement(seq):
+ '''Returns the reverse complement of a DNA sequence as string'''
+
+ compdict = {
+ 'a': 't',
+ 'c': 'g',
+ 'g': 'c',
+ 't': 'a',
+ 'u': 't',
+ 'm': 'k',
+ 'r': 'y',
+ 'w': 'w',
+ 's': 's',
+ 'y': 'r',
+ 'k': 'm',
+ 'v': 'b',
+ 'h': 'd',
+ 'd': 'h',
+ 'b': 'v',
+ 'x': 'x',
+ 'n': 'n',
+ 'A': 'T',
+ 'C': 'G',
+ 'G': 'C',
+ 'T': 'A',
+ 'U': 'T',
+ 'M': 'K',
+ 'R': 'Y',
+ 'W': 'W',
+ 'S': 'S',
+ 'Y': 'R',
+ 'K': 'M',
+ 'V': 'B',
+ 'H': 'D',
+ 'D': 'H',
+ 'B': 'V',
+ 'X': 'X',
+ 'N': 'N',
+ '*': '*'
+ }
+
+ complseq = ''.join([compdict[base] for base in seq])
+ # python hack to reverse a list/string/etc
+ complseq = complseq[::-1]
+ return complseq
+
+
+def mask_sequence(seq, maskchar, fpos, tpos):
+ '''Given a sequence, mask it with maskchar starting at fpos (including) and
+ ending at tpos (excluding)
+ '''
+
+ if len(maskchar) > 1:
+ raise RuntimeError("Internal error: more than one character given to mask_sequence")
+ if fpos<0:
+ fpos = 0
+ if tpos > len(seq):
+ tpos = len(seq)
+
+ newseq = ''.join((seq[:fpos],maskchar*(tpos-fpos), seq[tpos:]))
+
+ return newseq
+
+
+def fragment_sequences(sequence, qualities, splitchar):
+ '''Works like split() on strings, except it does this on a sequence
+ and the corresponding list with quality values.
+ Returns a tuple for each fragment, each sublist has the fragment
+ sequence as first and the fragment qualities as second elemnt'''
+
+ # this is slow (due to zip and list appends... use an iterator over
+ # the sequence find find variations and splices on seq and qual
+
+ if len(sequence) != len(qualities):
+ print sequence, qualities
+ raise RuntimeError("Internal error: length of sequence and qualities don't match???")
+
+ retlist = ([])
+ if len(sequence) == 0:
+ return retlist
+
+ actseq = ([])
+ actqual = ([])
+ if sequence[0] != splitchar:
+ inseq = True
+ else:
+ inseq = False
+ for char,qual in zip(sequence,qualities):
+ if inseq:
+ if char != splitchar:
+ actseq.append(char)
+ actqual.append(qual)
+ else:
+ retlist.append((''.join(actseq), actqual))
+ actseq = ([])
+ actqual = ([])
+ inseq = False
+ else:
+ if char != splitchar:
+ inseq = True
+ actseq.append(char)
+ actqual.append(qual)
+
+ if inseq and len(actseq):
+ retlist.append((''.join(actseq), actqual))
+
+ return retlist
+
+
+def calc_subseq_boundaries(maskedseq, maskchar):
+ '''E.g.:
+ ........xxxxxxxx..........xxxxxxxxxxxxxxxxxxxxx.........
+ to
+ (0,8),(8,16),(16,26),(26,47),(47,56)
+ '''
+
+ blist = ([])
+ if len(maskedseq) == 0:
+ return blist
+
+ inmask = True
+ if maskedseq[0] != maskchar:
+ inmask = False
+
+ start = 0
+ for spos in range(len(maskedseq)):
+ if inmask and maskedseq[spos] != maskchar:
+ blist.append(([start,spos]))
+ start = spos
+ inmask = False
+ elif not inmask and maskedseq[spos] == maskchar:
+ blist.append(([start,spos]))
+ start = spos
+ inmask = True
+
+ blist.append(([start,spos+1]))
+
+ return blist
+
+
+def correct_for_smallhits(maskedseq, maskchar, linkername):
+ '''If partial hits were found, take preventive measure: grow
+ the masked areas by 20 bases in each direction
+ Returns either unchanged "maskedseq" or a new sequence
+ with some more characters masked.
+ '''
+ global linkerlengths
+
+ CEBUG = 0
+
+ if CEBUG : print "correct_for_smallhits"
+ if CEBUG : print "Masked seq\n", maskedseq
+ if CEBUG : print "Linkername: ", linkername
+
+ if len(maskedseq) == 0:
+ return maskedseq
+
+ growl=40
+ growl2=growl/2
+
+ boundaries = calc_subseq_boundaries(maskedseq,maskchar)
+ if CEBUG : print "Boundaries: ", boundaries
+
+ foundpartial = False
+ for bounds in boundaries:
+ if CEBUG : print "\tbounds: ", bounds
+ left, right = bounds
+ if left != 0 and right != len(maskedseq):
+ if maskedseq[left] == maskchar:
+ # allow 10% discrepancy
+ # -linkerlengths[linkername]/10
+ # that's a kind of safety net if there are slight sequencing
+ # errors in the linker itself
+ if right-left < linkerlengths[linkername]-linkerlengths[linkername]/10:
+ if CEBUG : print "\t\tPartial: found " + str(right-left) + " gaps, " + linkername + " is " + str(linkerlengths[linkername]) + " nt long."
+ foundpartial = True
+
+ if not foundpartial:
+ return maskedseq
+
+ # grow
+ newseq = ""
+ for bounds in boundaries:
+ if CEBUG : print "Bounds: ", bounds
+ left, right = bounds
+ if maskedseq[left] == maskchar:
+ newseq += maskedseq[left:right]
+ else:
+ clearstart = 0
+ if left > 0 :
+ clearstart = left+growl2
+ clearstop = len(maskedseq)
+ if right < len(maskedseq):
+ clearstop = right-growl2
+
+ if CEBUG : print "clearstart, clearstop: ",clearstart, clearstop
+
+ if clearstop <= clearstart:
+ newseq += maskchar * (right-left)
+ else:
+ if clearstart != left:
+ newseq += maskchar * growl2
+ newseq += maskedseq[clearstart:clearstop]
+ if clearstop != right:
+ newseq += maskchar * growl2
+
+ #print "newseq\n",newseq
+
+ return newseq
+
+
+def split_paired_end(data, sff_fh, seq_fh, qual_fh, xml_fh):
+ '''Splits a paired end read and writes sequences into FASTA, FASTA qual
+ and XML traceinfo file. Returns the number of sequences created.
+
+ As the linker sequence may be anywhere in the read, including the ends
+ and overlapping with bad quality sequence, we need to perform some
+ computing and eventually set new clip points.
+
+ If the resulting split yields only one sequence (because linker
+ was not present or overlapping with left or right clip), only one
+ sequence will be written with ".fn" appended to the name.
+
+ If the read can be split, two reads will be written. The side left of
+ the linker will be named ".r" and will be written in reverse complement
+ into the file to conform with what approximately all assemblers expect
+ when reading paired-end data: reads in forward direction in file. The side
+ right of the linker will be named ".f"
+
+ If SSAHA found partial linker (linker sequences < length of linker),
+ the sequences will get a "_pl" furthermore be cut back thoroughly.
+
+ If SSAHA found multiple occurences of the linker, the names will get an
+ additional "_mlc" within the name to show that there was "multiple
+ linker contamination".
+
+ For multiple or partial linker, the "good" parts of the reads are
+ stored with a ".part<number>" name, additionally they will not get
+ template information in the XML
+ '''
+
+ global ssahapematches
+
+ CEBUG = 0
+
+ maskchar = "#"
+
+ if CEBUG : print "Need to split: " + data['name']
+
+ numseqs = 0;
+ readname = data['name']
+ readlen = data['number_of_bases']
+
+ leftclip, rightclip = return_merged_clips(data)
+ seq, qual = get_read_data(data)
+
+ if CEBUG : print "Original read:\n",seq
+
+ maskedseq = seq
+ if leftclip > 0:
+ maskedseq = mask_sequence(maskedseq, maskchar, 0, leftclip-1)
+ if rightclip < len(maskedseq):
+ maskedseq = mask_sequence(maskedseq, maskchar, rightclip, len(maskedseq))
+
+ leftclip, rightclip = return_merged_clips(data)
+ readlen = data['number_of_bases']
+
+ if CEBUG : print "Readname:", readname
+ if CEBUG : print "Readlen:", readlen
+ if CEBUG : print "Num matches:", str(len(ssahapematches[data['name']]))
+ if CEBUG : print "matches:", ssahapematches[data['name']]
+
+ for match in ssahapematches[data['name']]:
+ score = int(match[0])
+ linkername = match[2]
+ leftreadhit = int(match[3])
+ rightreadhit = int(match[4])
+ #leftlinkerhit = int(match[5])
+ #rightlinkerhit = int(match[6])
+ #direction = match[7]
+ #hitlen = int(match[8])
+ #hitidentity = float(match[9])
+
+ if CEBUG : print match
+ if CEBUG : print "Match with score:", score
+ if CEBUG : print "Read before:\n", maskedseq
+ maskedseq = mask_sequence(maskedseq, maskchar, leftreadhit-1, rightreadhit)
+ if CEBUG : print "Masked seq:\n", maskedseq
+
+ correctedseq = correct_for_smallhits(maskedseq, maskchar, linkername)
+
+ if len(maskedseq) != len(correctedseq):
+ raise RuntimeError("Internal error: maskedseq != correctedseq")
+
+ partialhits = False
+ if correctedseq != maskedseq:
+ if CEBUG : print "Partial hits in", readname
+ if CEBUG : print "Original seq:\n", seq
+ if CEBUG : print "Masked seq:\n", maskedseq
+ if CEBUG : print "Corrected seq\n", correctedseq
+ partialhits = True
+ readname += "_pl"
+ maskedseq = correctedseq
+
+ fragments = fragment_sequences(maskedseq, qual, maskchar)
+
+ if CEBUG : print "Fragments (", len(fragments), "): ", fragments
+
+ mlcflag = False
+ #if len(ssahapematches[data['name']]) > 1:
+ # #print "Multi linker contamination"
+ # mlcflag = True
+ # readname += "_mlc"
+
+ if len(fragments) > 2:
+ if CEBUG : print "Multi linker contamination"
+ mlcflag = True
+ readname += "_mlc"
+
+
+ #print fragments
+ if mlcflag or partialhits:
+ fragcounter = 1
+ readname += ".part"
+ for frag in fragments:
+ actseq = frag[0]
+ if len(actseq) >= 20:
+ actqual = frag[1]
+ oname = readname + str(fragcounter)
+ #seq_fh.write(">"+oname+"\n")
+ #seq_fh.write(actseq+"\n")
+ #qual_fh.write(">"+oname+"\n")
+ #qual_fh.write(' '.join((str(q) for q in actqual)))
+ #qual_fh.write("\n")
+ write_sequence(oname,actseq,actqual,seq_fh,qual_fh)
+ to_print = [create_basic_xml_info(oname,sff_fh.name)]
+ # No clipping in XML ... the multiple and partial fragments
+ # are clipped "hard"
+ # No template ID and trace_end: we don't know the
+ # orientation of the frahments. Even if it were
+ # only two, the fact we had multiple linkers
+ # says something went wrong, so simply do not
+ # write any paired-end information for all these fragments
+ to_print.append(' </trace>\n')
+ xml_fh.write(''.join(to_print))
+ numseqs += 1
+ fragcounter += 1
+ else:
+ if len(fragments) >2:
+ raise RuntimeError("Unexpected: more than two fragments detected in " + readname + ". please contact the authors.")
+ # nothing will happen for 0 fragments
+ if len(fragments) == 1:
+ #print "Tada1"
+ boundaries = calc_subseq_boundaries(maskedseq,maskchar)
+ if len(boundaries) < 1 or len(boundaries) >3:
+ raise RuntimeError("Unexpected case: ", str(len(boundaries)), "boundaries for 1 fragment of ", readname)
+ if len(boundaries) == 3:
+ # case: mask char on both sides of sequence
+ #print "bounds3"
+ data['clip_adapter_left']=1+boundaries[0][1]
+ data['clip_adapter_right']=boundaries[2][0]
+ elif len(boundaries) == 2:
+ # case: mask char left or right of sequence
+ #print "bounds2",
+ if maskedseq[0] == maskchar :
+ # case: mask char left
+ #print "left"
+ data['clip_adapter_left']=1+boundaries[0][1]
+ else:
+ # case: mask char right
+ #print "right"
+ data['clip_adapter_right']=boundaries[1][0]
+ data['name'] = data['name'] + ".fn"
+ write_unpaired_read(data, sff_fh, seq_fh, qual_fh, xml_fh)
+ numseqs = 1
+ elif len(fragments) == 2:
+ #print "Tada2"
+ oname = readname + ".r"
+ seq, qual = get_read_data(data)
+
+ startsearch = False
+ for spos in range(len(maskedseq)):
+ if maskedseq[spos] != maskchar:
+ startsearch = True;
+ else:
+ if startsearch:
+ break
+
+ #print "\nspos: ", spos
+ lseq=seq[:spos]
+ #print "lseq:", lseq
+ actseq = reverse_complement(lseq)
+ lreadlen = len(actseq)
+ lqual = qual[:spos];
+ # python hack to reverse a list/string/etc
+ lqual = lqual[::-1];
+
+ #seq_fh.write(">"+oname+"\n")
+ #seq_fh.write(actseq+"\n")
+ #qual_fh.write(">"+oname+"\n")
+ #qual_fh.write(' '.join((str(q) for q in lqual)))
+ #qual_fh.write("\n")
+
+ write_sequence(oname,actseq,lqual,seq_fh,qual_fh)
+
+ to_print = [create_basic_xml_info(oname,sff_fh.name)]
+ to_print.append(create_clip_xml_info(lreadlen, 0, lreadlen+1-data['clip_adapter_left'], 0, lreadlen+1-data['clip_qual_left']));
+ to_print.append(' <template_id>')
+ to_print.append(readname)
+ to_print.append('</template_id>\n')
+ to_print.append(' <trace_end>r</trace_end>\n')
+ to_print.append(' </trace>\n')
+ xml_fh.write(''.join(to_print))
+
+ oname = readname + ".f"
+ startsearch = False
+ for spos in range(len(maskedseq)-1,-1,-1):
+ if maskedseq[spos] != maskchar:
+ startsearch = True;
+ else:
+ if startsearch:
+ break
+
+ actseq = seq[spos+1:]
+ actqual = qual[spos+1:];
+
+ #print "\nspos: ", spos
+ #print "rseq:", actseq
+
+ #seq_fh.write(">"+oname+"\n")
+ #seq_fh.write(actseq+"\n")
+ #qual_fh.write(">"+oname+"\n")
+ #qual_fh.write(' '.join((str(q) for q in actqual)))
+ #qual_fh.write("\n")
+ write_sequence(oname,actseq,actqual,seq_fh,qual_fh)
+
+ rreadlen = len(actseq)
+ to_print = [create_basic_xml_info(oname,sff_fh.name)]
+ to_print.append(create_clip_xml_info(rreadlen, 0, rreadlen-(readlen-data['clip_adapter_right']), 0, rreadlen-(readlen-data['clip_qual_right'])));
+ to_print.append(' <template_id>')
+ to_print.append(readname)
+ to_print.append('</template_id>\n')
+ to_print.append(' <trace_end>f</trace_end>\n')
+ to_print.append(' </trace>\n')
+ xml_fh.write(''.join(to_print))
+ numseqs = 2
+
+ return numseqs
+
+
+
+def extract_reads_from_sff(config, sff_files):
+ '''Given the configuration and the list of sff_files it writes the seqs,
+ qualities and ancillary data into the output file(s).
+
+ If file for paired-end linker was given, first extracts all sequences
+ of an SFF and searches these against the linker(s) with SSAHA2 to
+ create needed information to split reads.
+ '''
+
+ global ssahapematches
+
+
+ if len(sff_files) == 0 :
+ raise RuntimeError("No SFF file given?")
+
+ #we go through all input files
+ for sff_file in sff_files:
+ if not os.path.getsize(sff_file):
+ raise RuntimeError('Empty file? : ' + sff_file)
+ fh = open(sff_file, 'r')
+ fh.close()
+
+ openmode = 'w'
+ if config['append']:
+ openmode = 'a'
+
+ seq_fh = open(config['seq_fname'], openmode)
+ xml_fh = open(config['xml_fname'], openmode)
+ if config['want_fastq']:
+ qual_fh = None
+ try:
+ os.remove(config['qual_fname'])
+ except :
+ python_formattingwithoutbracesisdumb_dummy = 1
+ else:
+ qual_fh = open(config['qual_fname'], openmode)
+
+ if not config['append']:
+ xml_fh.write('<?xml version="1.0"?>\n<trace_volume>\n')
+ else:
+ remove_last_xmltag_in_file(config['xml_fname'], "trace_volume")
+
+ #we go through all input files
+ for sff_file in sff_files:
+ #print "Working on '" + sff_file + "':"
+ ssahapematches.clear()
+
+ seqcheckstore = ([])
+
+ debug = 0
+
+ if not debug and config['pelinker_fname']:
+ #print "Creating temporary sequences from reads in '" + sff_file + "' ... ",
+ sys.stdout.flush()
+
+ if 0 :
+ # for debugging
+ pid = os.getpid()
+ tmpfasta_fname = 'sffe.tmp.'+ str(pid)+'.fasta'
+ tmpfasta_fh = open(tmpfasta_fname, 'w')
+ else:
+ tmpfasta_fh = tempfile.NamedTemporaryFile(prefix = 'sffeseqs_',
+ suffix = '.fasta')
+
+ sff_fh = open(sff_file, 'rb')
+ header_data = read_header(fileh=sff_fh)
+ for seq_data in sequences(fileh=sff_fh, header=header_data):
+ seq,qual = get_read_data(seq_data)
+ seqstring, qualstring = format_as_fasta(seq_data['name'],seq,qual)
+ tmpfasta_fh.write(seqstring)
+ #seq, qual, anci = extract_read_info(seq_data, sff_fh.name)
+ #tmpfasta_fh.write(seq)
+ #print "done."
+ tmpfasta_fh.seek(0)
+
+ if 0 :
+ # for debugging
+ tmpssaha_fname = 'sffe.tmp.'+str(pid)+'.ssaha2'
+ tmpssaha_fh = open(tmpssaha_fname, 'w+')
+ else:
+ tmpssaha_fh = tempfile.NamedTemporaryFile(prefix = 'sffealig_',
+ suffix = '.ssaha2')
+
+ launch_ssaha(config['pelinker_fname'], tmpfasta_fh.name, tmpssaha_fh)
+ tmpfasta_fh.close()
+
+ tmpssaha_fh.seek(0)
+ read_ssaha_data(tmpssaha_fh)
+ tmpssaha_fh.close()
+
+ if debug:
+ tmpssaha_fh = open("sffe.tmp.10634.ssaha2", 'r')
+ read_ssaha_data(tmpssaha_fh)
+
+ #print "Converting '" + sff_file + "' ... ",
+ sys.stdout.flush()
+ sff_fh = open(sff_file, 'rb')
+ #read_header(infile)
+ header_data = read_header(fileh=sff_fh)
+
+ #now convert all reads
+ nseqs_sff = 0
+ nseqs_out = 0
+ for seq_data in sequences(fileh=sff_fh, header=header_data):
+ nseqs_sff += 1
+
+ seq, qual = clip_read(seq_data)
+ seqcheckstore.append(seq[0:50])
+
+ #if nseqs_sff >1000:
+ # check_for_dubious_startseq(seqcheckstore,sff_file,seq_data)
+ # sys.exit()
+
+ if ssahapematches.has_key(seq_data['name']):
+ #print "Paired end:",seq_data['name']
+ nseqs_out += split_paired_end(seq_data, sff_fh, seq_fh, qual_fh, xml_fh)
+ else:
+ #print "Normal:",seq_data['name']
+ if config['pelinker_fname']:
+ seq_data['name'] = seq_data['name'] + ".fn"
+ write_unpaired_read(seq_data, sff_fh, seq_fh, qual_fh, xml_fh)
+ nseqs_out += 1
+ #print "done."
+ #print 'Converted', str(nseqs_sff), 'reads into', str(nseqs_out), 'sequences.'
+ sff_fh.close()
+
+ check_for_dubious_startseq(seqcheckstore,sff_file,seq_data)
+ seqcheckstore = ([])
+
+ xml_fh.write('</trace_volume>\n')
+
+ xml_fh.close()
+ seq_fh.close()
+ if qual_fh is not None:
+ qual_fh.close()
+
+ return
+
+def check_for_dubious_startseq(seqcheckstore, sffname,seqdata):
+
+ global stern_warning
+
+ foundproblem = ""
+ for checklen in range(1,len(seqcheckstore[0])):
+ foundinloop = False
+ seqdict = {}
+ for seq in seqcheckstore:
+ shortseq = seq[0:checklen]
+ if shortseq in seqdict:
+ seqdict[shortseq] += 1
+ else:
+ seqdict[shortseq] = 1
+
+ for shortseq, count in seqdict.items():
+ if float(count)/len(seqcheckstore) >= 0.5:
+ foundinloop = True
+ stern_warning
+ foundproblem = "\n"+"*" * 80
+ foundproblem += "\nWARNING: "
+ foundproblem += "weird sequences in file " + sffname + "\n\n"
+ foundproblem += "After applying left clips, " + str(count) + " sequences (="
+ foundproblem += '%.0f'%(100.0*float(count)/len(seqcheckstore))
+ foundproblem += "%) start with these bases:\n" + shortseq
+ foundproblem += "\n\nThis does not look sane.\n\n"
+ foundproblem += "Countermeasures you *probably* must take:\n"
+ foundproblem += "1) Make your sequence provider aware of that problem and ask whether this can be\n corrected in the SFF.\n"
+ foundproblem += "2) If you decide that this is not normal and your sequence provider does not\n react, use the --min_left_clip of sff_extract.\n"
+ left,right = return_merged_clips(seqdata)
+ foundproblem += " (Probably '--min_left_clip="+ str(left+len(shortseq))+"' but you should cross-check that)\n"
+ foundproblem += "*" * 80 + "\n"
+ if not foundinloop :
+ break
+ if len(foundproblem):
+ print foundproblem
+
+
+def parse_extra_info(info):
+ '''It parses the information that will go in the xml file.
+
+ There are two formats accepted for the extra information:
+ key1:value1, key2:value2
+ or:
+ file1.sff{key1:value1, key2:value2};file2.sff{key3:value3}
+ '''
+ if not info:
+ return info
+ finfos = info.split(';') #information for each file
+ data_for_files = {}
+ for finfo in finfos:
+ #we split the file name from the rest
+ items = finfo.split('{')
+ if len(items) == 1:
+ fname = fake_sff_name
+ info = items[0]
+ else:
+ fname = items[0]
+ info = items[1]
+ #now we get each key,value pair in the info
+ info = info.replace('}', '')
+ data = {}
+ for item in info.split(','):
+ key, value = item.strip().split(':')
+ key = key.strip()
+ value = value.strip()
+ data[key] = value
+ data_for_files[fname] = data
+ return data_for_files
+
+
+def return_merged_clips(data):
+ '''It returns the left and right positions to clip.'''
+ def max(a, b):
+ '''It returns the max of the two given numbers.
+
+ It won't take into account the zero values.
+ '''
+ if not a and not b:
+ return None
+ if not a:
+ return b
+ if not b:
+ return a
+ if a >= b:
+ return a
+ else:
+ return b
+ def min(a, b):
+ '''It returns the min of the two given numbers.
+
+ It won't take into account the zero values.
+ '''
+ if not a and not b:
+ return None
+ if not a:
+ return b
+ if not b:
+ return a
+ if a <= b:
+ return a
+ else:
+ return b
+ left = max(data['clip_adapter_left'], data['clip_qual_left'])
+ right = min(data['clip_adapter_right'], data['clip_qual_right'])
+ #maybe both clips where zero
+ if left is None:
+ left = 1
+ if right is None:
+ right = data['number_of_bases']
+ return left, right
+
+def sequence_case(data):
+ '''Given the data for one read it returns the seq with mixed case.
+
+ The regions to be clipped will be lower case and the rest upper case.
+ '''
+ left, right = return_merged_clips(data)
+ seq = data['bases']
+ new_seq = ''.join((seq[:left-1].lower(), seq[left-1:right], seq[right:].lower()))
+ return new_seq
+
+def clip_read(data):
+ '''Given the data for one read it returns clipped seq and qual.'''
+
+ qual = data['quality_scores']
+ left, right = return_merged_clips(data)
+ seq = data['bases']
+ qual = data['quality_scores']
+ new_seq = seq[left-1:right]
+ new_qual = qual[left-1:right]
+
+ return new_seq, new_qual
+
+
+
+def tests_for_ssaha(linker_fname):
+ '''Tests whether SSAHA2 can be successfully called.'''
+
+ try:
+ print "Testing whether SSAHA2 is installed and can be launched ... ",
+ sys.stdout.flush()
+ fh = open('/dev/null', 'w')
+ retcode = subprocess.call(["ssaha2", "-v"], stdout = fh)
+ fh.close()
+ print "ok."
+ except :
+ print "nope? Uh oh ...\n\n"
+ raise RuntimeError('Could not launch ssaha2. Have you installed it? Is it in your path?')
+
+
+def load_linker_sequences(linker_fname):
+ '''Loads all linker sequences into memory, storing only the length
+ of each linker.'''
+
+ global linkerlengths
+
+ if not os.path.getsize(linker_fname):
+ raise RuntimeError("File empty? '" + linker_fname + "'")
+ fh = open(linker_fname, 'r')
+ linkerseqs = read_fasta(fh)
+ if len(linkerseqs) == 0:
+ raise RuntimeError(linker_fname + ": no sequence found?")
+ for i in linkerseqs:
+ if linkerlengths.has_key(i.name):
+ raise RuntimeError(linker_fname + ": sequence '" + i.name + "' present multiple times. Aborting.")
+ linkerlengths[i.name] = len(i.sequence)
+ fh.close()
+
+
+def launch_ssaha(linker_fname, query_fname, output_fh):
+ '''Launches SSAHA2 on the linker and query file, string SSAHA2 output
+ into the output filehandle'''
+
+ try:
+ print "Searching linker sequences with SSAHA2 (this may take a while) ... ",
+ sys.stdout.flush()
+ retcode = subprocess.call(["ssaha2", "-output", "ssaha2", "-solexa", "-kmer", "4", "-skip", "1", linker_fname, query_fname], stdout = output_fh)
+ if retcode:
+ raise RuntimeError('Ups.')
+ else:
+ print "ok."
+ except:
+ print "\n"
+ raise RuntimeError('An error occured during the SSAHA2 execution, aborting.')
+
+def read_ssaha_data(ssahadata_fh):
+ '''Given file handle, reads file generated with SSAHA2 (with default
+ output format) and stores all matches as list ssahapematches
+ (ssaha paired-end matches) dictionary'''
+
+ global ssahapematches
+
+ print "Parsing SSAHA2 result file ... ",
+ sys.stdout.flush()
+
+ for line in ssahadata_fh:
+ if line.startswith('ALIGNMENT'):
+ ml = line.split()
+ if len(ml) != 12 :
+ print "\n", line,
+ raise RuntimeError('Expected 12 elements in the SSAHA2 line with ALIGMENT keyword, but found ' + str(len(ml)))
+ if not ssahapematches.has_key(ml[2]) :
+ ssahapematches[ml[2]] = ([])
+ if ml[8] == 'F':
+ #print line,
+
+ # store everything except the first element (output
+ # format name (ALIGNMENT)) and the last element
+ # (length)
+ ssahapematches[ml[2]].append(ml[1:-1])
+ else:
+ #print ml
+ ml[4],ml[5] = ml[5],ml[4]
+ #print ml
+ ssahapematches[ml[2]].append(ml[1:-1])
+
+ print "done."
+
+
+##########################################################################
+#
+# BaCh: This block was shamelessly copied from
+# http://python.genedrift.org/2007/07/04/reading-fasta-files-conclusion/
+# and then subsequently modified to read fasta correctly
+# It's still not fool proof, but should be good enough
+#
+##########################################################################
+
+class Fasta:
+ def __init__(self, name, sequence):
+ self.name = name
+ self.sequence = sequence
+
+def read_fasta(file):
+ items = []
+ aninstance = Fasta('', '')
+ linenum = 0
+ for line in file:
+ linenum += 1
+ if line.startswith(">"):
+ if len(aninstance.sequence):
+ items.append(aninstance)
+ aninstance = Fasta('', '')
+ # name == all characters until the first whitespace
+ # (split()[0]) but without the starting ">" ([1:])
+ aninstance.name = line.split()[0][1:]
+ aninstance.sequence = ''
+ if len(aninstance.name) == 0:
+ raise RuntimeError(file.name + ': no name in line ' + str(linenum) + '?')
+
+ else:
+ if len(aninstance.name) == 0:
+ raise RuntimeError(file.name + ': no sequence header at line ' + str(linenum) + '?')
+ aninstance.sequence += line.strip()
+
+ if len(aninstance.name) and len(aninstance.sequence):
+ items.append(aninstance)
+
+ return items
+##########################################################################
+
+def version_string ():
+ return "sff_extract " + __version__
+
+def read_config():
+ '''It reads the configuration options from the command line arguments and
+ it returns a dict with them.'''
+ from optparse import OptionParser, OptionGroup
+ usage = "usage: %prog [options] sff1 sff2 ..."
+ desc = "Extract sequences from 454 SFF files into FASTA, FASTA quality"\
+ " and XML traceinfo format. When a paired-end linker sequence"\
+ " is given (-l), use SSAHA2 to scan the sequences for the linker,"\
+ " then split the sequences, removing the linker."
+ parser = OptionParser(usage = usage, version = version_string(), description = desc)
+ parser.add_option('-a', '--append', action="store_true", dest='append',
+ help='append output to existing files', default=False)
+ parser.add_option('-i', '--xml_info', dest='xml_info',
+ help='extra info to write in the xml file')
+ parser.add_option("-l", "--linker_file", dest="pelinker_fname",
+ help="FASTA file with paired-end linker sequences", metavar="FILE")
+
+ group = OptionGroup(parser, "File name options","")
+ group.add_option('-c', '--clip', action="store_true", dest='clip',
+ help='clip (completely remove) ends with low qual and/or adaptor sequence', default=False)
+ group.add_option('-u', '--upper_case', action="store_false", dest='mix_case',
+ help='all bases in upper case, including clipped ends', default=True)
+ group.add_option('', '--min_left_clip', dest='min_leftclip',
+ metavar="INTEGER", type = "int",
+ help='if the left clip coming from the SFF is smaller than this value, override it', default=0)
+ group.add_option('-Q', '--fastq', action="store_true", dest='want_fastq',
+ help='store as FASTQ file instead of FASTA + FASTA quality file', default=False)
+ parser.add_option_group(group)
+
+ group = OptionGroup(parser, "File name options","")
+ group.add_option("-o", "--out_basename", dest="basename",
+ help="base name for all output files")
+ group.add_option("-s", "--seq_file", dest="seq_fname",
+ help="output sequence file name", metavar="FILE")
+ group.add_option("-q", "--qual_file", dest="qual_fname",
+ help="output quality file name", metavar="FILE")
+ group.add_option("-x", "--xml_file", dest="xml_fname",
+ help="output ancillary xml file name", metavar="FILE")
+ parser.add_option_group(group)
+
+ #default fnames
+ #is there an sff file?
+ basename = 'reads'
+ if sys.argv[-1][-4:].lower() == '.sff':
+ basename = sys.argv[-1][:-4]
+ def_seq_fname = basename + '.fasta'
+ def_qual_fname = basename + '.fasta.qual'
+ def_xml_fname = basename + '.xml'
+ def_pelinker_fname = ''
+ parser.set_defaults(seq_fname = def_seq_fname)
+ parser.set_defaults(qual_fname = def_qual_fname)
+ parser.set_defaults(xml_fname = def_xml_fname)
+ parser.set_defaults(pelinker_fname = def_pelinker_fname)
+
+ #we parse the cmd line
+ (options, args) = parser.parse_args()
+
+ #we put the result in a dict
+ global config
+ config = {}
+ for property in dir(options):
+ if property[0] == '_' or property in ('ensure_value', 'read_file',
+ 'read_module'):
+ continue
+ config[property] = getattr(options, property)
+
+ if config['basename'] is None:
+ config['basename']=basename
+
+ #if we have not set a file name with -s, -q or -x we set the basename
+ #based file name
+ if config['want_fastq']:
+ config['qual_fname'] = ''
+ if config['seq_fname'] == def_seq_fname:
+ config['seq_fname'] = config['basename'] + '.fastq'
+ else:
+ if config['seq_fname'] == def_seq_fname:
+ config['seq_fname'] = config['basename'] + '.fasta'
+ if config['qual_fname'] == def_qual_fname:
+ config['qual_fname'] = config['basename'] + '.fasta.qual'
+
+ if config['xml_fname'] == def_xml_fname:
+ config['xml_fname'] = config['basename'] + '.xml'
+
+ #we parse the extra info for the xml file
+ config['xml_info'] = parse_extra_info(config['xml_info'])
+ return config, args
+
+
+
+##########################################################################
+
+
+def testsome():
+ sys.exit()
+ return
+
+
+def debug():
+ try:
+ dummy = 1
+ #debug()
+ #testsome()
+
+ config, args = read_config()
+ load_linker_sequences(config['pelinker_fname'])
+
+ #pid = os.getpid()
+ pid = 15603
+
+ #tmpfasta_fname = 'sffe.tmp.'+ str(pid)+'.fasta'
+ #tmpfasta_fh = open(tmpfasta_fname, 'w')
+ tmpfasta_fname = 'FLVI58L05.fa'
+ tmpfasta_fh = open(tmpfasta_fname, 'r')
+
+ tmpssaha_fname = 'sffe.tmp.'+str(pid)+'.ssaha2'
+ tmpssaha_fh = open(tmpssaha_fname, 'w')
+
+ launch_ssaha(config['pelinker_fname'], tmpfasta_fh.name, tmpssaha_fh)
+
+ tmpssaha_fh = open("sffe.tmp.15603.ssaha2", 'r')
+ read_ssaha_data(tmpssaha_fh)
+
+ sys.exit()
+
+ extract_reads_from_sff(config, args)
+
+ except (OSError, IOError, RuntimeError), errval:
+ print errval
+ sys.exit()
+
+ sys.exit()
+
+
+def main():
+
+ argv = sys.argv
+ if len(argv) == 1:
+ sys.argv.append('-h')
+ read_config()
+ sys.exit()
+ try:
+ #debug();
+
+ config, args = read_config()
+
+ if config['pelinker_fname']:
+ #tests_for_ssaha(config['pelinker_fname'])
+ load_linker_sequences(config['pelinker_fname'])
+ if len(args) == 0:
+ raise RuntimeError("No SFF file given?")
+ extract_reads_from_sff(config, args)
+ except (OSError, IOError, RuntimeError), errval:
+ print errval
+ return 1
+
+ if stern_warning:
+ return 1
+
+ return 0
+
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff -r ebfc9236bf5a -r 61b09dc1dff2 tools/filters/sff_extractor.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/filters/sff_extractor.xml Wed Apr 07 11:12:00 2010 -0400
@@ -0,0 +1,22 @@
+<tool id="Sff_extractor" name="SFF converter" version="1.0.0">
+ <description></description>
+ <command interpreter="python">sff_extract.py -s $out_file1 -q $out_file2 -x $out_file3 $input</command>
+ <inputs>
+ <param format="sff" name="input" type="data" label="Extract from this dataset"/>
+ </inputs>
+ <outputs>
+ <data format="fasta" name="out_file1" />
+ <data format="qual" name="out_file2" />
+ <data format="xml" name="out_file3" />
+ </outputs>
+ <help>
+**What it does**
+
+This tool extracts data from the 454 Sequencer SFF format and creates three files containing the:
+ Sequences (FASTA),
+ Qualities (QUAL) and
+ Clippings (XML)
+ </help>
+</tool>
+
+
1
0
15 Apr '10
details: http://www.bx.psu.edu/hg/galaxy/rev/ebfc9236bf5a
changeset: 3618:ebfc9236bf5a
user: Anton Nekrutenko <anton(a)bx.psu.edu>
date: Wed Apr 07 08:44:57 2010 -0400
description:
Updated output format for solid2fastq converter
diffstat:
tools/next_gen_conversion/solid2fastq.xml | 4 ++--
tools/samtools/pileup_parser.xml | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diffs (26 lines):
diff -r 8fa4e8c12dfc -r ebfc9236bf5a tools/next_gen_conversion/solid2fastq.xml
--- a/tools/next_gen_conversion/solid2fastq.xml Tue Apr 06 19:00:39 2010 -0400
+++ b/tools/next_gen_conversion/solid2fastq.xml Wed Apr 07 08:44:57 2010 -0400
@@ -35,8 +35,8 @@
</param>
</inputs>
<outputs>
- <data format="fastqsanger" name="out_file1"/>
- <data format="fastqsanger" name="out_file2">
+ <data format="fastqcssanger" name="out_file1"/>
+ <data format="fastqcssanger" name="out_file2">
<filter>is_run['paired'] == 'yes'</filter>
</data>
</outputs>
diff -r 8fa4e8c12dfc -r ebfc9236bf5a tools/samtools/pileup_parser.xml
--- a/tools/samtools/pileup_parser.xml Tue Apr 06 19:00:39 2010 -0400
+++ b/tools/samtools/pileup_parser.xml Wed Apr 07 08:44:57 2010 -0400
@@ -353,7 +353,7 @@
**Example 3**: Report everything and print total number of differences
-If you set the **Print total number of differences?** to **Yes** the tool will print an additional column with the total number of reads where a devinat base is above the quality threshold cutoff. So, seetiing parametrs like this:
+If you set the **Print total number of differences?** to **Yes** the tool will print an additional column with the total number of reads where a devinat base is above the quality threshold. So, seetiing parametrs like this:
.. image:: ../static/images/pileup_parser_help3.png
1
0
15 Apr '10
details: http://www.bx.psu.edu/hg/galaxy/rev/8fa4e8c12dfc
changeset: 3617:8fa4e8c12dfc
user: Kanwei Li <kanwei(a)gmail.com>
date: Tue Apr 06 19:00:39 2010 -0400
description:
Add BedGraph datatype (wiggle-like data for intervals)
trackster:
- Fix various UI issues
- BedGraph -> array_tree converter
diffstat:
datatypes_conf.xml.sample | 3 +
lib/galaxy/datatypes/converters/bedgraph_to_array_tree_converter.py | 55 ++++++++++
lib/galaxy/datatypes/converters/bedgraph_to_array_tree_converter.xml | 14 ++
lib/galaxy/datatypes/interval.py | 9 +-
lib/galaxy/visualization/tracks/data/array_tree.py | 2 +-
lib/galaxy/web/controllers/tracks.py | 6 +-
static/scripts/packed/trackster.js | 2 +-
static/scripts/trackster.js | 34 +++---
templates/tracks/browser.mako | 19 +--
9 files changed, 110 insertions(+), 34 deletions(-)
diffs (334 lines):
diff -r 2d415fb320b4 -r 8fa4e8c12dfc datatypes_conf.xml.sample
--- a/datatypes_conf.xml.sample Tue Apr 06 15:29:23 2010 -0400
+++ b/datatypes_conf.xml.sample Tue Apr 06 19:00:39 2010 -0400
@@ -16,6 +16,9 @@
<!-- <display file="ucsc/interval_as_bed.xml" /> -->
<display file="genetrack.xml" />
</datatype>
+ <datatype extension="bedgraph" type="galaxy.datatypes.interval:BedGraph" display_in_upload="true">
+ <converter file="bedgraph_to_array_tree_converter.xml" target_datatype="array_tree"/>
+ </datatype>
<datatype extension="bedstrict" type="galaxy.datatypes.interval:BedStrict" />
<datatype extension="bed6" type="galaxy.datatypes.interval:Bed6">
<converter file="bed_to_genetrack_converter.xml" target_datatype="genetrack"/>
diff -r 2d415fb320b4 -r 8fa4e8c12dfc lib/galaxy/datatypes/converters/bedgraph_to_array_tree_converter.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/galaxy/datatypes/converters/bedgraph_to_array_tree_converter.py Tue Apr 06 19:00:39 2010 -0400
@@ -0,0 +1,55 @@
+#!/usr/bin/env python
+
+from __future__ import division
+
+import sys
+from galaxy import eggs
+import pkg_resources; pkg_resources.require( "bx-python" )
+from bx.arrays.array_tree import *
+# from bx.arrays.wiggle import BedReader
+
+BLOCK_SIZE = 100
+
+class BedGraphReader:
+ def __init__( self, f ):
+ self.f = f
+
+ def __iter__( self ):
+ return self
+
+ def next( self ):
+ while True:
+ line = self.f.readline()
+ if not line:
+ raise StopIteration()
+ if line.isspace():
+ continue
+ if line[0] == "#":
+ continue
+ if line[0].isalpha():
+ if line.startswith( "track" ) or line.startswith( "browser" ):
+ continue
+
+ feature = line.strip().split()
+ chrom = feature[0]
+ chrom_start = int(feature[1])
+ chrom_end = int(feature[2])
+ score = int(feature[3])
+ return chrom, chrom_start, chrom_end, None, score
+def main():
+
+ input_fname = sys.argv[1]
+ out_fname = sys.argv[2]
+
+ reader = BedGraphReader( open( input_fname ) )
+
+ # Fill array from reader
+ d = array_tree_dict_from_reader( reader, {}, block_size = BLOCK_SIZE )
+
+ for array_tree in d.itervalues():
+ array_tree.root.build_summary()
+
+ FileArrayTreeDict.dict_to_file( d, open( out_fname, "w" ) )
+
+if __name__ == "__main__":
+ main()
\ No newline at end of file
diff -r 2d415fb320b4 -r 8fa4e8c12dfc lib/galaxy/datatypes/converters/bedgraph_to_array_tree_converter.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/galaxy/datatypes/converters/bedgraph_to_array_tree_converter.xml Tue Apr 06 19:00:39 2010 -0400
@@ -0,0 +1,14 @@
+<tool id="CONVERTER_BedGraph_0" name="Index BedGraph for Track Viewer">
+ <!-- Used internally to generate track indexes -->
+ <command interpreter="python">bedgraph_to_array_tree_converter.py $input $output</command>
+ <inputs>
+ <page>
+ <param format="bedgraph" name="input" type="data" label="Choose BedGraph"/>
+ </page>
+ </inputs>
+ <outputs>
+ <data format="array_tree" name="output"/>
+ </outputs>
+ <help>
+ </help>
+</tool>
\ No newline at end of file
diff -r 2d415fb320b4 -r 8fa4e8c12dfc lib/galaxy/datatypes/interval.py
--- a/lib/galaxy/datatypes/interval.py Tue Apr 06 15:29:23 2010 -0400
+++ b/lib/galaxy/datatypes/interval.py Tue Apr 06 19:00:39 2010 -0400
@@ -345,7 +345,14 @@
def get_track_resolution( self, dataset, start, end):
return None
-
+class BedGraph( Interval ):
+ """Tab delimited chrom/start/end/datavalue dataset"""
+
+ file_ext = "bedgraph"
+
+ def get_track_type( self ):
+ return "LineTrack", {"data": "array_tree"}
+
class Bed( Interval ):
"""Tab delimited data in BED format"""
file_ext = "bed"
diff -r 2d415fb320b4 -r 8fa4e8c12dfc lib/galaxy/visualization/tracks/data/array_tree.py
--- a/lib/galaxy/visualization/tracks/data/array_tree.py Tue Apr 06 15:29:23 2010 -0400
+++ b/lib/galaxy/visualization/tracks/data/array_tree.py Tue Apr 06 19:00:39 2010 -0400
@@ -19,7 +19,7 @@
chrom_array_tree = d[chrom]
except KeyError:
f.close()
- return "no data"
+ return None
root_summary = chrom_array_tree.get_summary( 0, chrom_array_tree.levels )
diff -r 2d415fb320b4 -r 8fa4e8c12dfc lib/galaxy/web/controllers/tracks.py
--- a/lib/galaxy/web/controllers/tracks.py Tue Apr 06 15:29:23 2010 -0400
+++ b/lib/galaxy/web/controllers/tracks.py Tue Apr 06 19:00:39 2010 -0400
@@ -209,11 +209,13 @@
return messages.NO_CONVERTER
# Need to check states again for the converted version
+ if converted_dataset and converted_dataset.state == model.Dataset.states.ERROR:
+ return messages.ERROR
+
if not converted_dataset or converted_dataset.state != model.Dataset.states.OK:
return messages.PENDING
- if converted_dataset.state == model.Dataset.states.ERROR:
- return messages.ERROR
+
extra_info = None
if 'index' in data_sources:
diff -r 2d415fb320b4 -r 8fa4e8c12dfc static/scripts/packed/trackster.js
--- a/static/scripts/packed/trackster.js Tue Apr 06 15:29:23 2010 -0400
+++ b/static/scripts/packed/trackster.js Tue Apr 06 19:00:39 2010 -0400
@@ -1,1 +1,1 @@
-var DEBUG=false;var DENSITY=1000,FEATURE_LEVELS=10,DATA_ERROR="There was an error in indexing this dataset.",DATA_NOCONVERTER="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",DATA_NONE="No data for this chrom/contig.",DATA_PENDING="Currently indexing... please wait",DATA_LOADING="Loading data...",CACHED_TILES_FEATURE=10,CACHED_TILES_LINE=30,CACHED_DATA=20,CONTEXT=$("<canvas></canvas>").get(0).getContext("2d"),RIGHT_STRAND,LEFT_STRAND;var right_img=new Image();right_img.src="../images/visualization/strand_right.png";right_img.onload=function(){RIGHT_STRAND=CONTEXT.createPattern(right_img,"repeat")};var left_img=new Image();left_img.src="../images/visualization/strand_left.png";left_img.onload=function(){LEFT_STRAND=CONTEXT.createPattern(left_img,"repeat")};var right_img_inv=new Image();right_img_inv.src="../images/visualization/strand_right_inv.png";right_img_inv.onload=function(){RIGHT_STRAND_INV=CONTEXT.createPattern(right_img_inv!
,"repeat")};var left_img_inv=new Image();left_img_inv.src="../images/visualization/strand_left_inv.png";left_img_inv.onload=function(){LEFT_STRAND_INV=CONTEXT.createPattern(left_img_inv,"repeat")};function commatize(b){b+="";var a=/(\d+)(\d{3})/;while(a.test(b)){b=b.replace(a,"$1,$2")}return b}var Cache=function(a){this.num_elements=a;this.clear()};$.extend(Cache.prototype,{get:function(b){var a=this.key_ary.indexOf(b);if(a!=-1){this.key_ary.splice(a,1);this.key_ary.push(b)}return this.obj_cache[b]},set:function(b,c){if(!this.obj_cache[b]){if(this.key_ary.length>=this.num_elements){var a=this.key_ary.shift();delete this.obj_cache[a]}this.key_ary.push(b)}this.obj_cache[b]=c;return c},clear:function(){this.obj_cache={};this.key_ary=[]}});var Drawer=function(){};$.extend(Drawer.prototype,{intensity:function(b,a,c){},});drawer=new Drawer();var View=function(b,d,c,a){this.vis_id=c;this.dbkey=a;this.title=d;this.chrom=b;this.tracks=[];this.label_tracks=[];this.max_low=0;this.max_!
high=0;this.center=(this.max_high-this.max_low)/2;this.zoom_factor=3;t
his.zoom_level=0;this.track_id_counter=0};$.extend(View.prototype,{add_track:function(a){a.view=this;a.track_id=this.track_id_counter;this.tracks.push(a);if(a.init){a.init()}a.container_div.attr("id","track_"+a.track_id);this.track_id_counter+=1},add_label_track:function(a){a.view=this;this.label_tracks.push(a)},remove_track:function(a){a.container_div.fadeOut("slow",function(){$(this).remove()});delete this.tracks[a]},update_options:function(){var b=$("ul#sortable-ul").sortable("toArray");var d=[];var c=$("#viewport > div").sort(function(g,f){return b.indexOf($(g).attr("id"))>b.indexOf($(f).attr("id"))});$("#viewport > div").remove();$("#viewport").html(c);for(var e in view.tracks){var a=view.tracks[e];if(a.update_options){a.update_options(e)}}},reset:function(){this.low=this.max_low;this.high=this.max_high;this.center=this.center=(this.max_high-this.max_low)/2;this.zoom_level=0;$(".yaxislabel").remove()},redraw:function(f){this.span=this.max_high-this.max_low;var d=this.sp!
an/Math.pow(this.zoom_factor,this.zoom_level),b=this.center-(d/2),e=b+d;if(b<0){b=0;e=b+d}else{if(e>this.max_high){e=this.max_high;b=e-d}}this.low=Math.floor(b);this.high=Math.ceil(e);this.center=Math.round(this.low+(this.high-this.low)/2);this.resolution=Math.pow(10,Math.ceil(Math.log((this.high-this.low)/200)/Math.LN10));this.zoom_res=Math.pow(FEATURE_LEVELS,Math.max(0,Math.ceil(Math.log(this.resolution,FEATURE_LEVELS)/Math.log(FEATURE_LEVELS))));$("#overview-box").css({left:(this.low/this.span)*$("#overview-viewport").width(),width:Math.max(12,((this.high-this.low)/this.span)*$("#overview-viewport").width())}).show();$("#low").val(commatize(this.low));$("#high").val(commatize(this.high));if(!f){for(var c=0,a=this.tracks.length;c<a;c++){if(this.tracks[c].enabled){this.tracks[c].draw()}}for(var c=0,a=this.label_tracks.length;c<a;c++){this.label_tracks[c].draw()}}},zoom_in:function(a,b){if(this.max_high===0||this.high-this.low<30){return}if(a){this.center=a/b.width()*(this.!
high-this.low)+this.low}this.zoom_level+=1;this.redraw()},zoom_out:fun
ction(){if(this.max_high===0){return}if(this.zoom_level<=0){this.zoom_level=0;return}this.zoom_level-=1;this.redraw()}});var Track=function(a,b){this.name=a;this.parent_element=b;this.init_global()};$.extend(Track.prototype,{init_global:function(){this.header_div=$("<div class='track-header'>").text(this.name);this.content_div=$("<div class='track-content'>");this.container_div=$("<div></div>").addClass("track").append(this.header_div).append(this.content_div);this.parent_element.append(this.container_div)},init_each:function(c,b){var a=this;a.enabled=false;a.data_queue={};a.tile_cache.clear();a.data_cache.clear();a.content_div.css("height","30px");a.content_div.text(DATA_LOADING);a.container_div.removeClass("nodata error pending");if(a.view.chrom){$.getJSON(data_url,c,function(d){if(!d||d==="error"){a.container_div.addClass("error");a.content_div.text(DATA_ERROR)}else{if(d==="no converter"){a.container_div.addClass("error");a.content_div.text(DATA_NOCONVERTER)}else{if((d.da!
ta&&d.data.length===0)||d==="no data"){a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}else{if(d==="pending"){a.container_div.addClass("pending");a.content_div.text(DATA_PENDING);setTimeout(function(){a.init()},5000)}else{a.content_div.text("");a.content_div.css("height",a.height_px+"px");a.enabled=true;b(d);a.draw()}}}}})}else{a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}}});var TiledTrack=function(){};$.extend(TiledTrack.prototype,Track.prototype,{draw:function(){var i=this.view.low,e=this.view.high,f=e-i,d=this.view.resolution;if(DEBUG){$("#debug").text(d+" "+this.view.zoom_res)}var k=$("<div style='position: relative;'></div>");this.content_div.children(":first").remove();this.content_div.append(k);var l=this.content_div.width()/f;var h;var a=Math.floor(i/d/DENSITY);while((a*DENSITY*d)<e){var j=this.content_div.width()+"_"+this.view.zoom_level+"_"+a;var c=this.tile_cache.get(j);if(c){var g=a*DENSITY*d;var b=(g-i)*l;if(this.left_offse!
t){b-=this.left_offset}c.css({left:b});k.append(c);this.max_height=Mat
h.max(this.max_height,c.height())}else{this.delayed_draw(this,j,i,e,a,d,k,l)}a+=1}},delayed_draw:function(c,e,a,f,b,d,g,h){setTimeout(function(){if(!(a>c.view.high||f<c.view.low)){tile_element=c.draw_tile(d,b,g,h);if(tile_element){c.tile_cache.set(e,tile_element);c.max_height=Math.max(c.max_height,tile_element.height());c.content_div.css("height",c.max_height+"px")}}},50)}});var LabelTrack=function(a){Track.call(this,null,a);this.track_type="LabelTrack";this.hidden=true;this.container_div.addClass("label-track")};$.extend(LabelTrack.prototype,Track.prototype,{draw:function(){var c=this.view,d=c.high-c.low,g=Math.floor(Math.pow(10,Math.floor(Math.log(d)/Math.log(10)))),a=Math.floor(c.low/g)*g,e=this.content_div.width(),b=$("<div style='position: relative; height: 1.3em;'></div>");while(a<c.high){var f=(a-c.low)/d*e;b.append($("<div class='label'>"+commatize(a)+"</div>").css({position:"absolute",left:f-1}));a+=g}this.content_div.children(":first").remove();this.content_div.app!
end(b)}});var LineTrack=function(c,a,b){this.track_type="LineTrack";Track.call(this,c,$("#viewport"));TiledTrack.call(this);this.height_px=100;this.container_div.addClass("line-track");this.dataset_id=a;this.data_cache=new Cache(CACHED_DATA);this.tile_cache=new Cache(CACHED_TILES_LINE);this.prefs={min_value:undefined,max_value:undefined,mode:"line"};if(b.min_value!==undefined){this.prefs.min_value=b.min_value}if(b.max_value!==undefined){this.prefs.max_value=b.max_value}if(b.max_value!==undefined){this.prefs.mode=b.mode}};$.extend(LineTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.tracks.indexOf(a);a.vertical_range=undefined;this.init_each({stats:true,chrom:a.view.chrom,low:null,high:null,dataset_id:a.dataset_id},function(c){data=c.data;if(isNaN(parseFloat(a.prefs.min_value))||isNaN(parseFloat(a.prefs.max_value))){a.prefs.min_value=data.min;a.prefs.max_value=data.max;$("#track_"+b+"_minval").val(a.prefs.min_value);$("#track_"+b+"_maxval").val(a.pre!
fs.max_value)}a.vertical_range=a.prefs.max_value-a.prefs.min_value;a.t
otal_frequency=data.total_frequency;$("#linetrack_"+b+"_minval").remove();$("#linetrack_"+b+"_maxval").remove();var e=$("<div></div>").addClass("yaxislabel").attr("id","linetrack_"+b+"_minval").text(a.prefs.min_value);var d=$("<div></div>").addClass("yaxislabel").attr("id","linetrack_"+b+"_maxval").text(a.prefs.max_value);d.css({position:"relative",top:"25px"});d.prependTo(a.container_div);e.css({position:"relative",top:a.height_px+55+"px"});e.prependTo(a.container_div)})},get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:data_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dataset_id:this.dataset_id,resolution:this.view.resolution},success:function(g){data=g.data;c.data_cache.set(e,data);delete c.data_queue[e];c.draw()},error:function(h,g,i){console.log(h,g,i)}})}},draw_tile:function(o,q,c,e){if(this.vertical_range===undefined){return}var r=q*DENSITY*o,a=DENSITY*o,b=$("<canvas class!
='tile'></canvas>"),u=o+"_"+q;if(!this.data_cache.get(u)){this.get_data(o,q);return}var t=this.data_cache.get(u);b.css({position:"absolute",top:0,left:(r-this.view.low)*e});b.get(0).width=Math.ceil(a*e);b.get(0).height=this.height_px;var n=b.get(0).getContext("2d"),k=false,l=this.prefs.min_value,g=this.prefs.max_value,m=this.vertical_range,s=this.total_frequency,d=this.height_px;n.beginPath();if(t.length>1){var f=Math.ceil((t[1][0]-t[0][0])*e)}else{var f=10}for(var p=0;p<t.length;p++){var j=t[p][0]-r;var h=t[p][1];if(this.prefs.mode=="intensity"){if(h===null){continue}j=j*e;if(h<=l){h=l}else{if(h>=g){h=g}}h=255-Math.floor((h-l)/m*255);n.fillStyle="rgb("+h+","+h+","+h+")";n.fillRect(j,0,f,30)}else{if(h===null){k=false;continue}else{j=j*e;if(h<=l){h=l}else{if(h>=g){h=g}}h=Math.round(d-(h-l)/m*d);if(k){n.lineTo(j,h)}else{n.moveTo(j,h);k=true}}}}n.stroke();c.append(b);return b},gen_options:function(n){var a=$("<div></div>").addClass("form-row");var h="track_"+n+"_minval",k="tra!
ck_"+n+"_maxval",e="track_"+n+"_mode",l=$("<label></label>").attr("for
",h).text("Min value:"),b=(this.prefs.min_value===undefined?"":this.prefs.min_value),m=$("<input></input>").attr("id",h).val(b),g=$("<label></label>").attr("for",k).text("Max value:"),j=(this.prefs.max_value===undefined?"":this.prefs.max_value),f=$("<input></input>").attr("id",k).val(j),d=$("<label></label>").attr("for",e).text("Display mode:"),i=(this.prefs.mode===undefined?"line":this.prefs.mode),c=$('<select id="'+e+'"><option value="line" id="mode_line">Line</option><option value="intensity" id="mode_intensity">Intensity</option></select>');$("#"+e+" #mode_"+i).attr("selected","selected");return a.append(l).append(m).append(g).append(f).append(d).append(c)},update_options:function(d){var a=$("#track_"+d+"_minval").val(),c=$("#track_"+d+"_maxval").val(),b=$("#track_"+d+"_mode option:selected").val();if(a!==this.prefs.min_value||c!==this.prefs.max_value||b!=this.prefs.mode){this.prefs.min_value=parseFloat(a);this.prefs.max_value=parseFloat(c);this.prefs.mode=b;this.vertica!
l_range=this.prefs.max_value-this.prefs.min_value;$("#linetrack_"+d+"_minval").text(this.prefs.min_value);$("#linetrack_"+d+"_maxval").text(this.prefs.max_value);this.tile_cache.clear();this.draw()}}});var FeatureTrack=function(c,a,b){this.track_type="FeatureTrack";Track.call(this,c,$("#viewport"));TiledTrack.call(this);this.height_px=100;this.container_div.addClass("feature-track");this.dataset_id=a;this.zo_slots={};this.show_labels_scale=0.001;this.showing_details=false;this.vertical_detail_px=10;this.vertical_nodetail_px=3;this.default_font="9px Monaco, Lucida Console, monospace";this.left_offset=200;this.inc_slots={};this.data_queue={};this.s_e_by_tile={};this.tile_cache=new Cache(CACHED_TILES_FEATURE);this.data_cache=new Cache(20);this.prefs={block_color:"black",label_color:"black",show_counts:true};if(b.block_color!==undefined){this.prefs.block_color=b.block_color}if(b.label_color!==undefined){this.prefs.label_color=b.label_color}if(b.show_counts!==undefined){this.pre!
fs.show_counts=b.show_counts}};$.extend(FeatureTrack.prototype,TiledTr
ack.prototype,{init:function(){var a=this,b=a.view.max_low+"_"+a.view.max_high;this.init_each({low:a.view.max_low,high:a.view.max_high,dataset_id:a.dataset_id,chrom:a.view.chrom,resolution:this.view.resolution},function(c){a.data_cache.set(b,c);a.draw()})},get_data:function(a,d){var b=this,c=a+"_"+d;if(!b.data_queue[c]){b.data_queue[c]=true;$.getJSON(data_url,{chrom:b.view.chrom,low:a,high:d,dataset_id:b.dataset_id,resolution:this.view.resolution},function(e){b.data_cache.set(c,e);delete b.data_queue[c];b.draw()})}},incremental_slots:function(a,g,c){if(!this.inc_slots[a]){this.inc_slots[a]={};this.inc_slots[a].w_scale=1/a;this.s_e_by_tile[a]={}}var m=this.inc_slots[a].w_scale,u=[],h=0,b=$("<canvas></canvas>").get(0).getContext("2d"),n=this.view.max_low;var d,f,w=[];for(var r=0,s=g.length;r<s;r++){var e=g[r],l=e[0];if(this.inc_slots[a][l]!==undefined){h=Math.max(h,this.inc_slots[a][l]);w.push(this.inc_slots[a][l])}else{u.push(r)}}for(var r=0,s=u.length;r<s;r++){var e=g[u[r]];!
l=e[0],feature_start=e[1],feature_end=e[2],feature_name=e[3];d=Math.floor((feature_start-n)*m);if(!c){d-=b.measureText(feature_name).width}f=Math.ceil((feature_end-n)*m);var q=0;while(true){var o=true;if(this.s_e_by_tile[a][q]!==undefined){for(var p=0,v=this.s_e_by_tile[a][q].length;p<v;p++){var t=this.s_e_by_tile[a][q][p];if(f>t[0]&&d<t[1]){o=false;break}}}if(o){if(this.s_e_by_tile[a][q]===undefined){this.s_e_by_tile[a][q]=[]}this.s_e_by_tile[a][q].push([d,f]);this.inc_slots[a][l]=q;h=Math.max(h,q);break}q++}}return h},draw_tile:function(R,h,m,ae){var z=h*DENSITY*R,X=(h+1)*DENSITY*R,w=DENSITY*R;var ac,ad,p;var Y=z+"_"+X;var ac=this.data_cache.get(Y);if(!ac){this.data_queue[[z,X]]=true;this.get_data(z,X);return}if(ac.dataset_type=="array_tree"){p=30}else{var P=(ac.extra_info==="no_detail");var af=(P?this.vertical_nodetail_px:this.vertical_detail_px);p=this.incremental_slots(this.view.zoom_res,ac.data,P)*af+15;m.parent().css("height",Math.max(this.height_px,p)+"px");ad=this.!
inc_slots[this.view.zoom_res]}var a=Math.ceil(w*ae),F=$("<canvas class
='tile'></canvas>"),T=this.prefs.label_color,f=this.prefs.block_color,J=this.left_offset;F.css({position:"absolute",top:0,left:(z-this.view.low)*ae-J});F.get(0).width=a+J;F.get(0).height=p;var t=F.get(0).getContext("2d");t.fillStyle=this.prefs.block_color;t.font=this.default_font;t.textAlign="right";var C=55,W=255-C,g=W*2/3;if(ac.dataset_type=="summary_tree"){var L=ac.data;var v=ac.max;var l=ac.avg;if(ac.data.length>2){var b=Math.ceil((L[1][0]-L[0][0])*ae)}else{var b=50}for(var aa=0,s=L.length;aa<s;aa++){var N=Math.ceil((L[aa][0]-z)*ae);var M=L[aa][1];if(!M){continue}var E=Math.floor(W-(M/v)*W);t.fillStyle="rgb("+E+","+E+","+E+")";t.fillRect(N+J,0,b,20);if(this.prefs.show_counts){if(E>g){t.fillStyle="black"}else{t.fillStyle="#ddd"}t.textAlign="center";t.fillText(L[aa][1],N+J+(b/2),12)}}m.append(F);return F}var ac=ac.data;var Z=0;for(var aa=0,s=ac.length;aa<s;aa++){var G=ac[aa],D=G[0],ab=G[1],O=G[2],A=G[3];if(ab<=X&&O>=z){var Q=Math.floor(Math.max(0,(ab-z)*ae)),u=Math.ceil(Ma!
th.min(a,(O-z)*ae)),K=ad[D]*af;if(P){t.fillRect(Q+J,K+5,u-Q,1)}else{var r=G[4],I=G[5],S=G[6],e=G[7];var q,U,B=null,ag=null;if(I&&S){B=Math.floor(Math.max(0,(I-z)*ae));ag=Math.ceil(Math.min(a,(S-z)*ae))}if(ab>z){t.fillStyle=T;t.fillText(A,Q-1+J,K+8);t.fillStyle=f}if(e){if(r){if(r=="+"){t.fillStyle=RIGHT_STRAND}else{if(r=="-"){t.fillStyle=LEFT_STRAND}}t.fillRect(Q+J,K,u-Q,10);t.fillStyle=f}for(var Y=0,d=e.length;Y<d;Y++){var n=e[Y],c=Math.floor(Math.max(0,(n[0]-z)*ae)),H=Math.ceil(Math.min(a,(n[1]-z)*ae));if(c>H){continue}q=5;U=3;t.fillRect(c+J,K+U,H-c,q);if(B!==undefined&&!(c>ag||H<B)){q=9;U=1;var V=Math.max(c,B),o=Math.min(H,ag);t.fillRect(V+J,K+U,o-V,q)}}}else{q=9;U=1;t.fillRect(Q+J,K+U,u-Q,q);if(G.strand){if(G.strand=="+"){t.fillStyle=RIGHT_STRAND_INV}else{if(G.strand=="-"){t.fillStyle=LEFT_STRAND_INV}}t.fillRect(Q+J,K,u-Q,10);t.fillStyle=prefs.block_color}}}Z++}}m.append(F);return F},gen_options:function(h){var a=$("<div></div>").addClass("form-row");var d="track_"+h+"_b!
lock_color",j=$("<label></label>").attr("for",d).text("Block color:"),
k=$("<input></input>").attr("id",d).attr("name",d).val(this.prefs.block_color),i="track_"+h+"_label_color",f=$("<label></label>").attr("for",i).text("Text color:"),g=$("<input></input>").attr("id",i).attr("name",i).val(this.prefs.label_color),e="track_"+h+"_show_count",c=$("<label></label>").attr("for",e).text("Show summary counts"),b=$('<input type="checkbox" style="float:left;"></input>').attr("id",e).attr("name",e).attr("checked",this.prefs.show_counts);return a.append(j).append(k).append(f).append(g).append(b).append(c)},update_options:function(d){var b=$("#track_"+d+"_block_color").val(),c=$("#track_"+d+"_label_color").val(),a=$("#track_"+d+"_show_count").attr("checked");if(b!==this.prefs.block_color||c!==this.prefs.label_color||a!=this.prefs.show_counts){this.prefs.block_color=b;this.prefs.label_color=c;this.prefs.show_counts=a;this.tile_cache.clear();this.draw()}}});var ReadTrack=function(c,a,b){FeatureTrack.call(this,c,a,b);this.track_type="ReadTrack"};$.extend(ReadT!
rack.prototype,TiledTrack.prototype,FeatureTrack.prototype,{});
\ No newline at end of file
+var DEBUG=false;var DENSITY=1000,FEATURE_LEVELS=10,DATA_ERROR="There was an error in indexing this dataset.",DATA_NOCONVERTER="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",DATA_NONE="No data for this chrom/contig.",DATA_PENDING="Currently indexing... please wait",DATA_LOADING="Loading data...",CACHED_TILES_FEATURE=10,CACHED_TILES_LINE=30,CACHED_DATA=20,CONTEXT=$("<canvas></canvas>").get(0).getContext("2d"),RIGHT_STRAND,LEFT_STRAND;var right_img=new Image();right_img.src="../images/visualization/strand_right.png";right_img.onload=function(){RIGHT_STRAND=CONTEXT.createPattern(right_img,"repeat")};var left_img=new Image();left_img.src="../images/visualization/strand_left.png";left_img.onload=function(){LEFT_STRAND=CONTEXT.createPattern(left_img,"repeat")};var right_img_inv=new Image();right_img_inv.src="../images/visualization/strand_right_inv.png";right_img_inv.onload=function(){RIGHT_STRAND_INV=CONTEXT.createPattern(right_img_inv!
,"repeat")};var left_img_inv=new Image();left_img_inv.src="../images/visualization/strand_left_inv.png";left_img_inv.onload=function(){LEFT_STRAND_INV=CONTEXT.createPattern(left_img_inv,"repeat")};function commatize(b){b+="";var a=/(\d+)(\d{3})/;while(a.test(b)){b=b.replace(a,"$1,$2")}return b}var Cache=function(a){this.num_elements=a;this.clear()};$.extend(Cache.prototype,{get:function(b){var a=this.key_ary.indexOf(b);if(a!=-1){this.key_ary.splice(a,1);this.key_ary.push(b)}return this.obj_cache[b]},set:function(b,c){if(!this.obj_cache[b]){if(this.key_ary.length>=this.num_elements){var a=this.key_ary.shift();delete this.obj_cache[a]}this.key_ary.push(b)}this.obj_cache[b]=c;return c},clear:function(){this.obj_cache={};this.key_ary=[]}});var Drawer=function(){};$.extend(Drawer.prototype,{intensity:function(b,a,c){},});drawer=new Drawer();var View=function(b,d,c,a){this.vis_id=c;this.dbkey=a;this.title=d;this.chrom=b;this.tracks=[];this.label_tracks=[];this.max_low=0;this.max_!
high=0;this.center=(this.max_high-this.max_low)/2;this.zoom_factor=3;t
his.zoom_level=0;this.track_id_counter=0};$.extend(View.prototype,{add_track:function(a){a.view=this;a.track_id=this.track_id_counter;this.tracks.push(a);if(a.init){a.init()}a.container_div.attr("id","track_"+a.track_id);this.track_id_counter+=1},add_label_track:function(a){a.view=this;this.label_tracks.push(a)},remove_track:function(a){a.container_div.fadeOut("slow",function(){$(this).remove()});delete this.tracks[a]},update_options:function(){var b=$("ul#sortable-ul").sortable("toArray");var d=[];var c=$("#viewport > div").sort(function(g,f){return b.indexOf($(g).attr("id"))>b.indexOf($(f).attr("id"))});$("#viewport > div").remove();$("#viewport").html(c);for(var e in view.tracks){var a=view.tracks[e];if(a.update_options){a.update_options(e)}}},reset:function(){this.low=this.max_low;this.high=this.max_high;this.center=this.center=(this.max_high-this.max_low)/2;this.zoom_level=0;$(".yaxislabel").remove()},redraw:function(f){this.span=this.max_high-this.max_low;var d=this.sp!
an/Math.pow(this.zoom_factor,this.zoom_level),b=this.center-(d/2),e=b+d;if(b<0){b=0;e=b+d}else{if(e>this.max_high){e=this.max_high;b=e-d}}this.low=Math.floor(b);this.high=Math.ceil(e);this.center=Math.round(this.low+(this.high-this.low)/2);this.resolution=Math.pow(10,Math.ceil(Math.log((this.high-this.low)/200)/Math.LN10));this.zoom_res=Math.pow(FEATURE_LEVELS,Math.max(0,Math.ceil(Math.log(this.resolution,FEATURE_LEVELS)/Math.log(FEATURE_LEVELS))));$("#overview-box").css({left:(this.low/this.span)*$("#overview-viewport").width(),width:Math.max(12,((this.high-this.low)/this.span)*$("#overview-viewport").width())}).show();$("#low").val(commatize(this.low));$("#high").val(commatize(this.high));if(!f){for(var c=0,a=this.tracks.length;c<a;c++){if(this.tracks[c].enabled){this.tracks[c].draw()}}for(var c=0,a=this.label_tracks.length;c<a;c++){this.label_tracks[c].draw()}}},zoom_in:function(a,b){if(this.max_high===0||this.high-this.low<30){return}if(a){this.center=a/b.width()*(this.!
high-this.low)+this.low}this.zoom_level+=1;this.redraw()},zoom_out:fun
ction(){if(this.max_high===0){return}if(this.zoom_level<=0){this.zoom_level=0;return}this.zoom_level-=1;this.redraw()}});var Track=function(a,b){this.name=a;this.parent_element=b;this.init_global()};$.extend(Track.prototype,{init_global:function(){this.header_div=$("<div class='track-header'>").text(this.name);this.content_div=$("<div class='track-content'>");this.container_div=$("<div></div>").addClass("track").append(this.header_div).append(this.content_div);this.parent_element.append(this.container_div)},init_each:function(c,b){var a=this;a.enabled=false;a.data_queue={};a.tile_cache.clear();a.data_cache.clear();a.content_div.css("height","30px");a.content_div.text(DATA_LOADING);a.container_div.removeClass("nodata error pending");if(a.view.chrom){$.getJSON(data_url,c,function(d){if(!d||d==="error"){a.container_div.addClass("error");a.content_div.text(DATA_ERROR)}else{if(d==="no converter"){a.container_div.addClass("error");a.content_div.text(DATA_NOCONVERTER)}else{if(d.dat!
a&&d.data.length===0||d.data===null){a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}else{if(d==="pending"){a.container_div.addClass("pending");a.content_div.text(DATA_PENDING);setTimeout(function(){a.init()},5000)}else{a.content_div.text("");a.content_div.css("height",a.height_px+"px");a.enabled=true;b(d);a.draw()}}}}})}else{a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}}});var TiledTrack=function(){};$.extend(TiledTrack.prototype,Track.prototype,{draw:function(){var i=this.view.low,e=this.view.high,f=e-i,d=this.view.resolution;if(DEBUG){$("#debug").text(d+" "+this.view.zoom_res)}var k=$("<div style='position: relative;'></div>");this.content_div.children(":first").remove();this.content_div.append(k);var l=this.content_div.width()/f;var h;var a=Math.floor(i/d/DENSITY);while((a*DENSITY*d)<e){var j=this.content_div.width()+"_"+this.view.zoom_level+"_"+a;var c=this.tile_cache.get(j);if(c){var g=a*DENSITY*d;var b=(g-i)*l;if(this.left_offset)!
{b-=this.left_offset}c.css({left:b});k.append(c);this.max_height=Math.
max(this.max_height,c.height())}else{this.delayed_draw(this,j,i,e,a,d,k,l)}a+=1}},delayed_draw:function(c,e,a,f,b,d,g,h){setTimeout(function(){if(!(a>c.view.high||f<c.view.low)){tile_element=c.draw_tile(d,b,g,h);if(tile_element){c.tile_cache.set(e,tile_element);c.max_height=Math.max(c.max_height,tile_element.height());c.content_div.css("height",c.max_height+"px")}}},50)}});var LabelTrack=function(a){Track.call(this,null,a);this.track_type="LabelTrack";this.hidden=true;this.container_div.addClass("label-track")};$.extend(LabelTrack.prototype,Track.prototype,{draw:function(){var c=this.view,d=c.high-c.low,g=Math.floor(Math.pow(10,Math.floor(Math.log(d)/Math.log(10)))),a=Math.floor(c.low/g)*g,e=this.content_div.width(),b=$("<div style='position: relative; height: 1.3em;'></div>");while(a<c.high){var f=(a-c.low)/d*e;b.append($("<div class='label'>"+commatize(a)+"</div>").css({position:"absolute",left:f-1}));a+=g}this.content_div.children(":first").remove();this.content_div.appen!
d(b)}});var LineTrack=function(c,a,b){this.track_type="LineTrack";Track.call(this,c,$("#viewport"));TiledTrack.call(this);this.height_px=100;this.container_div.addClass("line-track");this.dataset_id=a;this.data_cache=new Cache(CACHED_DATA);this.tile_cache=new Cache(CACHED_TILES_LINE);this.prefs={min_value:undefined,max_value:undefined,mode:"Line"};if(b.min_value!==undefined){this.prefs.min_value=b.min_value}if(b.max_value!==undefined){this.prefs.max_value=b.max_value}if(b.mode!==undefined){this.prefs.mode=b.mode}};$.extend(LineTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.tracks.indexOf(a);a.vertical_range=undefined;this.init_each({stats:true,chrom:a.view.chrom,low:null,high:null,dataset_id:a.dataset_id},function(c){data=c.data;if(isNaN(parseFloat(a.prefs.min_value))||isNaN(parseFloat(a.prefs.max_value))){a.prefs.min_value=data.min;a.prefs.max_value=data.max;$("#track_"+b+"_minval").val(a.prefs.min_value);$("#track_"+b+"_maxval").val(a.prefs.max_!
value)}a.vertical_range=a.prefs.max_value-a.prefs.min_value;a.total_fr
equency=data.total_frequency;$("#linetrack_"+b+"_minval").remove();$("#linetrack_"+b+"_maxval").remove();var e=$("<div></div>").addClass("yaxislabel").attr("id","linetrack_"+b+"_minval").text(a.prefs.min_value);var d=$("<div></div>").addClass("yaxislabel").attr("id","linetrack_"+b+"_maxval").text(a.prefs.max_value);d.css({position:"relative",top:"25px"});d.prependTo(a.container_div);e.css({position:"relative",top:a.height_px+55+"px"});e.prependTo(a.container_div)})},get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:data_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dataset_id:this.dataset_id,resolution:this.view.resolution},success:function(g){data=g.data;c.data_cache.set(e,data);delete c.data_queue[e];c.draw()},error:function(h,g,i){console.log(h,g,i)}})}},draw_tile:function(o,q,c,e){if(this.vertical_range===undefined){return}var r=q*DENSITY*o,a=DENSITY*o,b=$("<canvas class='tile'!
></canvas>"),u=o+"_"+q;if(this.data_cache.get(u)===undefined){this.get_data(o,q);return}var t=this.data_cache.get(u);if(t===null){return}b.css({position:"absolute",top:0,left:(r-this.view.low)*e});b.get(0).width=Math.ceil(a*e);b.get(0).height=this.height_px;var n=b.get(0).getContext("2d"),k=false,l=this.prefs.min_value,g=this.prefs.max_value,m=this.vertical_range,s=this.total_frequency,d=this.height_px;n.beginPath();if(t.length>1){var f=Math.ceil((t[1][0]-t[0][0])*e)}else{var f=10}for(var p=0;p<t.length;p++){var j=t[p][0]-r;var h=t[p][1];if(this.prefs.mode=="Intensity"){if(h===null){continue}j=j*e;if(h<=l){h=l}else{if(h>=g){h=g}}h=255-Math.floor((h-l)/m*255);n.fillStyle="rgb("+h+","+h+","+h+")";n.fillRect(j,0,f,this.height_px)}else{if(h===null){k=false;continue}else{j=j*e;if(h<=l){h=l}else{if(h>=g){h=g}}h=Math.round(d-(h-l)/m*d);if(k){n.lineTo(j,h)}else{n.moveTo(j,h);k=true}}}}n.stroke();c.append(b);return b},gen_options:function(n){var a=$("<div></div>").addClass("form-row!
");var h="track_"+n+"_minval",k="track_"+n+"_maxval",e="track_"+n+"_mo
de",l=$("<label></label>").attr("for",h).text("Min value:"),b=(this.prefs.min_value===undefined?"":this.prefs.min_value),m=$("<input></input>").attr("id",h).val(b),g=$("<label></label>").attr("for",k).text("Max value:"),j=(this.prefs.max_value===undefined?"":this.prefs.max_value),f=$("<input></input>").attr("id",k).val(j),d=$("<label></label>").attr("for",e).text("Display mode:"),i=(this.prefs.mode===undefined?"Line":this.prefs.mode),c=$('<select id="'+e+'"><option value="Line" id="mode_Line">Line</option><option value="Intensity" id="mode_Intensity">Intensity</option></select>');c.children("#mode_"+i).attr("selected","selected");return a.append(l).append(m).append(g).append(f).append(d).append(c)},update_options:function(d){var a=$("#track_"+d+"_minval").val(),c=$("#track_"+d+"_maxval").val(),b=$("#track_"+d+"_mode option:selected").val();if(a!==this.prefs.min_value||c!==this.prefs.max_value||b!=this.prefs.mode){this.prefs.min_value=parseFloat(a);this.prefs.max_value=parseF!
loat(c);this.prefs.mode=b;this.vertical_range=this.prefs.max_value-this.prefs.min_value;$("#linetrack_"+d+"_minval").text(this.prefs.min_value);$("#linetrack_"+d+"_maxval").text(this.prefs.max_value);this.tile_cache.clear();this.draw()}}});var FeatureTrack=function(c,a,b){this.track_type="FeatureTrack";Track.call(this,c,$("#viewport"));TiledTrack.call(this);this.height_px=100;this.container_div.addClass("feature-track");this.dataset_id=a;this.zo_slots={};this.show_labels_scale=0.001;this.showing_details=false;this.vertical_detail_px=10;this.vertical_nodetail_px=3;this.default_font="9px Monaco, Lucida Console, monospace";this.left_offset=200;this.inc_slots={};this.data_queue={};this.s_e_by_tile={};this.tile_cache=new Cache(CACHED_TILES_FEATURE);this.data_cache=new Cache(20);this.prefs={block_color:"black",label_color:"black",show_counts:false};if(b.block_color!==undefined){this.prefs.block_color=b.block_color}if(b.label_color!==undefined){this.prefs.label_color=b.label_color!
}if(b.show_counts!==undefined){this.prefs.show_counts=b.show_counts}};
$.extend(FeatureTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.max_low+"_"+a.view.max_high;this.init_each({low:a.view.max_low,high:a.view.max_high,dataset_id:a.dataset_id,chrom:a.view.chrom,resolution:this.view.resolution},function(c){a.data_cache.set(b,c);a.draw()})},get_data:function(a,d){var b=this,c=a+"_"+d;if(!b.data_queue[c]){b.data_queue[c]=true;$.getJSON(data_url,{chrom:b.view.chrom,low:a,high:d,dataset_id:b.dataset_id,resolution:this.view.resolution},function(e){b.data_cache.set(c,e);delete b.data_queue[c];b.draw()})}},incremental_slots:function(a,g,c){if(!this.inc_slots[a]){this.inc_slots[a]={};this.inc_slots[a].w_scale=1/a;this.s_e_by_tile[a]={}}var m=this.inc_slots[a].w_scale,u=[],h=0,b=$("<canvas></canvas>").get(0).getContext("2d"),n=this.view.max_low;var d,f,w=[];for(var r=0,s=g.length;r<s;r++){var e=g[r],l=e[0];if(this.inc_slots[a][l]!==undefined){h=Math.max(h,this.inc_slots[a][l]);w.push(this.inc_slots[a][l])}else{u.push(r)}}for(var!
r=0,s=u.length;r<s;r++){var e=g[u[r]];l=e[0],feature_start=e[1],feature_end=e[2],feature_name=e[3];d=Math.floor((feature_start-n)*m);if(!c){d-=b.measureText(feature_name).width}f=Math.ceil((feature_end-n)*m);var q=0;while(true){var o=true;if(this.s_e_by_tile[a][q]!==undefined){for(var p=0,v=this.s_e_by_tile[a][q].length;p<v;p++){var t=this.s_e_by_tile[a][q][p];if(f>t[0]&&d<t[1]){o=false;break}}}if(o){if(this.s_e_by_tile[a][q]===undefined){this.s_e_by_tile[a][q]=[]}this.s_e_by_tile[a][q].push([d,f]);this.inc_slots[a][l]=q;h=Math.max(h,q);break}q++}}return h},draw_tile:function(R,h,m,ae){var z=h*DENSITY*R,X=(h+1)*DENSITY*R,w=DENSITY*R;var ac,ad,p;var Y=z+"_"+X;var ac=this.data_cache.get(Y);if(ac===undefined){this.data_queue[[z,X]]=true;this.get_data(z,X);return}if(ac.dataset_type=="array_tree"){p=30}else{var P=(ac.extra_info==="no_detail");var af=(P?this.vertical_nodetail_px:this.vertical_detail_px);p=this.incremental_slots(this.view.zoom_res,ac.data,P)*af+15;m.parent().css(!
"height",Math.max(this.height_px,p)+"px");ad=this.inc_slots[this.view.
zoom_res]}var a=Math.ceil(w*ae),F=$("<canvas class='tile'></canvas>"),T=this.prefs.label_color,f=this.prefs.block_color,J=this.left_offset;F.css({position:"absolute",top:0,left:(z-this.view.low)*ae-J});F.get(0).width=a+J;F.get(0).height=p;var t=F.get(0).getContext("2d");t.fillStyle=this.prefs.block_color;t.font=this.default_font;t.textAlign="right";var C=55,W=255-C,g=W*2/3;if(ac.dataset_type=="summary_tree"){var L=ac.data;var v=ac.max;var l=ac.avg;if(ac.data.length>2){var b=Math.ceil((L[1][0]-L[0][0])*ae)}else{var b=50}for(var aa=0,s=L.length;aa<s;aa++){var N=Math.ceil((L[aa][0]-z)*ae);var M=L[aa][1];if(!M){continue}var E=Math.floor(W-(M/v)*W);t.fillStyle="rgb("+E+","+E+","+E+")";t.fillRect(N+J,0,b,20);if(this.prefs.show_counts){if(E>g){t.fillStyle="black"}else{t.fillStyle="#ddd"}t.textAlign="center";t.fillText(L[aa][1],N+J+(b/2),12)}}m.append(F);return F}var ac=ac.data;var Z=0;for(var aa=0,s=ac.length;aa<s;aa++){var G=ac[aa],D=G[0],ab=G[1],O=G[2],A=G[3];if(ab<=X&&O>=z){var !
Q=Math.floor(Math.max(0,(ab-z)*ae)),u=Math.ceil(Math.min(a,(O-z)*ae)),K=ad[D]*af;if(P){t.fillRect(Q+J,K+5,u-Q,1)}else{var r=G[4],I=G[5],S=G[6],e=G[7];var q,U,B=null,ag=null;if(I&&S){B=Math.floor(Math.max(0,(I-z)*ae));ag=Math.ceil(Math.min(a,(S-z)*ae))}if(ab>z){t.fillStyle=T;t.fillText(A,Q-1+J,K+8);t.fillStyle=f}if(e){if(r){if(r=="+"){t.fillStyle=RIGHT_STRAND}else{if(r=="-"){t.fillStyle=LEFT_STRAND}}t.fillRect(Q+J,K,u-Q,10);t.fillStyle=f}for(var Y=0,d=e.length;Y<d;Y++){var n=e[Y],c=Math.floor(Math.max(0,(n[0]-z)*ae)),H=Math.ceil(Math.min(a,(n[1]-z)*ae));if(c>H){continue}q=5;U=3;t.fillRect(c+J,K+U,H-c,q);if(B!==undefined&&!(c>ag||H<B)){q=9;U=1;var V=Math.max(c,B),o=Math.min(H,ag);t.fillRect(V+J,K+U,o-V,q)}}}else{q=9;U=1;t.fillRect(Q+J,K+U,u-Q,q);if(G.strand){if(G.strand=="+"){t.fillStyle=RIGHT_STRAND_INV}else{if(G.strand=="-"){t.fillStyle=LEFT_STRAND_INV}}t.fillRect(Q+J,K,u-Q,10);t.fillStyle=prefs.block_color}}}Z++}}m.append(F);return F},gen_options:function(i){var a=$("<div>!
</div>").addClass("form-row");var e="track_"+i+"_block_color",k=$("<la
bel></label>").attr("for",e).text("Block color:"),l=$("<input></input>").attr("id",e).attr("name",e).val(this.prefs.block_color),j="track_"+i+"_label_color",g=$("<label></label>").attr("for",j).text("Text color:"),h=$("<input></input>").attr("id",j).attr("name",j).val(this.prefs.label_color),f="track_"+i+"_show_count",c=$("<label></label>").attr("for",f).text("Show summary counts"),b=$('<input type="checkbox" style="float:left;"></input>').attr("id",f).attr("name",f).attr("checked",this.prefs.show_counts),d=$("<div></div>").append(b).append(c);return a.append(k).append(l).append(g).append(h).append(d)},update_options:function(d){var b=$("#track_"+d+"_block_color").val(),c=$("#track_"+d+"_label_color").val(),a=$("#track_"+d+"_show_count").attr("checked");if(b!==this.prefs.block_color||c!==this.prefs.label_color||a!=this.prefs.show_counts){this.prefs.block_color=b;this.prefs.label_color=c;this.prefs.show_counts=a;this.tile_cache.clear();this.draw()}}});var ReadTrack=function(c!
,a,b){FeatureTrack.call(this,c,a,b);this.track_type="ReadTrack"};$.extend(ReadTrack.prototype,TiledTrack.prototype,FeatureTrack.prototype,{});
\ No newline at end of file
diff -r 2d415fb320b4 -r 8fa4e8c12dfc static/scripts/trackster.js
--- a/static/scripts/trackster.js Tue Apr 06 15:29:23 2010 -0400
+++ b/static/scripts/trackster.js Tue Apr 06 19:00:39 2010 -0400
@@ -238,7 +238,7 @@
} else if (result === "no converter") {
track.container_div.addClass("error");
track.content_div.text(DATA_NOCONVERTER);
- } else if ( (result.data && result.data.length === 0) || result === "no data") {
+ } else if (result.data && result.data.length === 0 || result.data === null) {
track.container_div.addClass("nodata");
track.content_div.text(DATA_NONE);
} else if (result === "pending") {
@@ -354,10 +354,10 @@
this.dataset_id = dataset_id;
this.data_cache = new Cache(CACHED_DATA);
this.tile_cache = new Cache(CACHED_TILES_LINE);
- this.prefs = { 'min_value': undefined, 'max_value': undefined, 'mode': 'line' };
+ this.prefs = { 'min_value': undefined, 'max_value': undefined, 'mode': 'Line' };
if (prefs.min_value !== undefined) { this.prefs.min_value = prefs.min_value; }
if (prefs.max_value !== undefined) { this.prefs.max_value = prefs.max_value; }
- if (prefs.max_value !== undefined) { this.prefs.mode = prefs.mode; }
+ if (prefs.mode !== undefined) { this.prefs.mode = prefs.mode; }
};
$.extend( LineTrack.prototype, TiledTrack.prototype, {
init: function() {
@@ -431,12 +431,13 @@
canvas = $("<canvas class='tile'></canvas>"),
key = resolution + "_" + tile_index;
- if (!this.data_cache.get(key)) {
+ if (this.data_cache.get(key) === undefined) {
this.get_data( resolution, tile_index );
return;
}
var data = this.data_cache.get(key);
+ if (data === null) { return; }
canvas.css( {
position: "absolute",
@@ -467,7 +468,7 @@
var x = data[i][0] - tile_low;
var y = data[i][1];
- if ( this.prefs.mode == "intensity" ) {
+ if ( this.prefs.mode == "Intensity" ) {
// DRAW INTENSITY
if (y === null) {
continue;
@@ -480,7 +481,7 @@
}
y = 255 - Math.floor( (y - min_value) / vertical_range * 255 );
ctx.fillStyle = "rgb(" +y+ "," +y+ "," +y+ ")";
- ctx.fillRect(x, 0, delta_x_px, 30);
+ ctx.fillRect(x, 0, delta_x_px, this.height_px);
}
else {
// Missing data causes us to stop drawing
@@ -523,10 +524,11 @@
max_val = (this.prefs.max_value === undefined ? "" : this.prefs.max_value),
max_input = $('<input></input>').attr("id", maxval).val(max_val),
mode_label = $('<label></label>').attr("for", mode).text("Display mode:"),
- mode_val = (this.prefs.mode === undefined ? "line" : this.prefs.mode),
- mode_input = $('<select id="' +mode+ '"><option value="line" id="mode_line">Line</option><option value="intensity" id="mode_intensity">Intensity</option></select>');
- $("#" + mode + " #mode_"+mode_val).attr('selected', 'selected');
-
+ mode_val = (this.prefs.mode === undefined ? "Line" : this.prefs.mode),
+ mode_input = $('<select id="' +mode+ '"><option value="Line" id="mode_Line">Line</option><option value="Intensity" id="mode_Intensity">Intensity</option></select>');
+
+ mode_input.children("#mode_"+mode_val).attr('selected', 'selected');
+
return container.append(min_label).append(min_input).append(max_label).append(max_input).append(mode_label).append(mode_input);
}, update_options: function(track_id) {
var min_value = $('#track_' + track_id + '_minval').val(),
@@ -567,7 +569,7 @@
this.tile_cache = new Cache(CACHED_TILES_FEATURE);
this.data_cache = new Cache(20);
- this.prefs = { 'block_color': 'black', 'label_color': 'black', 'show_counts': true };
+ this.prefs = { 'block_color': 'black', 'label_color': 'black', 'show_counts': false };
if (prefs.block_color !== undefined) { this.prefs.block_color = prefs.block_color; }
if (prefs.label_color !== undefined) { this.prefs.label_color = prefs.label_color; }
if (prefs.show_counts !== undefined) { this.prefs.show_counts = prefs.show_counts; }
@@ -581,9 +583,6 @@
high: track.view.max_high, dataset_id: track.dataset_id,
chrom: track.view.chrom, resolution: this.view.resolution }, function (result) {
track.data_cache.set(key, result);
- // track.values = result;
- // track.calc_slots();
- // track.slots = track.zo_slots;
track.draw();
});
},
@@ -691,7 +690,7 @@
var k = tile_low + '_' + tile_high;
var data = this.data_cache.get(k);
- if (!data) {
+ if (data === undefined) {
this.data_queue[ [tile_low, tile_high] ] = true;
this.get_data(tile_low, tile_high);
return;
@@ -866,9 +865,10 @@
label_color_input = $('<input></input>').attr("id", label_color).attr("name", label_color).val(this.prefs.label_color),
show_count = 'track_' + track_id + '_show_count',
show_count_label = $('<label></label>').attr("for", show_count).text("Show summary counts"),
- show_count_input = $('<input type="checkbox" style="float:left;"></input>').attr("id", show_count).attr("name", show_count).attr("checked", this.prefs.show_counts);
+ show_count_input = $('<input type="checkbox" style="float:left;"></input>').attr("id", show_count).attr("name", show_count).attr("checked", this.prefs.show_counts),
+ show_count_div = $('<div></div>').append(show_count_input).append(show_count_label);
- return container.append(block_color_label).append(block_color_input).append(label_color_label).append(label_color_input).append(show_count_input).append(show_count_label);
+ return container.append(block_color_label).append(block_color_input).append(label_color_label).append(label_color_input).append(show_count_div);
}, update_options: function(track_id) {
var block_color = $('#track_' + track_id + '_block_color').val(),
label_color = $('#track_' + track_id + '_label_color').val(),
diff -r 2d415fb320b4 -r 8fa4e8c12dfc templates/tracks/browser.mako
--- a/templates/tracks/browser.mako Tue Apr 06 15:29:23 2010 -0400
+++ b/templates/tracks/browser.mako Tue Apr 06 19:00:39 2010 -0400
@@ -45,7 +45,7 @@
</div>
</div>
<div id="nav-controls">
- <form name="chr" id="chr" method="get">
+ <form action="#">
<select id="chrom" name="chrom" style="width: 15em;">
<option value="">Loading</option>
</select>
@@ -68,14 +68,14 @@
<div class="unified-panel-header" unselectable="on">
<div class="unified-panel-header-inner">Configuration</div>
</div>
- <form action="${h.url_for( action='update_config' )}">
+ <form action="#" onsubmit="view.update_options();return false;">
## <input name="title" id="title" value="${config.title}" />
<div id="show-hide-move">
<ul id="sortable-ul"></ul>
</div>
- <input type="button" id="refresh-button" value="Refresh" />
+ <input type="submit" id="refresh-button" value="Refresh" />
<input type="button" id="save-button" value="Save" />
- <input id="add-track" type="button" value="Add Track" />
+ <input id="add-track" type="button" value="Add Tracks" />
</form>
</%def>
@@ -189,10 +189,6 @@
view.redraw();
});
- $("#refresh-button").bind( "click", function(e) {
- view.update_options();
- });
-
// Use a popup grid to add more tracks
$("#add-track").bind( "click", function(e) {
$.ajax({
@@ -311,16 +307,15 @@
del_icon = $('<a href="#" class="icon-button delete" />'),
edit_icon = $('<a href="#" class="icon-button edit" />'),
body = $('<div class="historyItemBody"></div>'),
- li = $('<li class="sortable"></li>').attr("id", "track_" + track_id),
+ li = $('<li class="sortable"></li>').attr("id", "track_" + track_id + "_li"),
div = $('<div class="historyItemContainer historyItem"></div>'),
editable = $('<div style="display:none"></div>').attr("id", "track_" + track_id + "_editable");
edit_icon.bind("click", function() {
$("#track_" + track_id + "_editable").toggle();
});
-
del_icon.bind("click", function() {
- li.fadeOut('slow', function() { $(this).remove(); });
+ $("#track_" + track_id + "_li").fadeOut('slow', function() { $("#track_" + track_id).remove(); });
view.remove_track(track);
view.update_options();
});
@@ -329,7 +324,7 @@
if (track.gen_options) {
editable.append(track.gen_options(track_id)).appendTo(body);
}
- div.append(title).append(body).appendTo(li)
+ div.append(title).append(body).appendTo(li);
$("ul#sortable-ul").append(li);
}
};
1
0