galaxy-dev
Threads by month
- ----- 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
February 2010
- 32 participants
- 180 discussions
16 Feb '10
details: http://www.bx.psu.edu/hg/galaxy/rev/9f96c4fef396
changeset: 3396:9f96c4fef396
user: James Taylor <james(a)jamestaylor.org>
date: Mon Feb 15 17:39:06 2010 -0500
description:
Temporarily revent jquery and jquery.tipsy to 1.3 versions
diffstat:
static/scripts/jquery.js | 4518 +++++++++++++++++++++++++++++++-
static/scripts/jquery.tipsy.js | 60 +-
static/scripts/packed/jquery.js | 163 +-
static/scripts/packed/jquery.tipsy.js | 2 +-
4 files changed, 4439 insertions(+), 304 deletions(-)
diffs (truncated from 4803 to 3000 lines):
diff -r 04fa6cbeb28c -r 9f96c4fef396 static/scripts/jquery.js
--- a/static/scripts/jquery.js Mon Feb 15 16:56:02 2010 -0500
+++ b/static/scripts/jquery.js Mon Feb 15 17:39:06 2010 -0500
@@ -1,152 +1,4376 @@
/*!
- * jQuery JavaScript Library v1.4.1
+ * jQuery JavaScript Library v1.3.2
* http://jquery.com/
*
- * Copyright 2010, John Resig
- * Dual licensed under the MIT or GPL Version 2 licenses.
- * http://jquery.org/license
+ * Copyright (c) 2009 John Resig
+ * Dual licensed under the MIT and GPL licenses.
+ * http://docs.jquery.com/License
*
- * Includes Sizzle.js
- * http://sizzlejs.com/
- * Copyright 2010, The Dojo Foundation
- * Released under the MIT, BSD, and GPL Licenses.
- *
- * Date: Mon Jan 25 19:43:33 2010 -0500
+ * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)
+ * Revision: 6246
*/
-(function(z,v){function la(){if(!c.isReady){try{r.documentElement.doScroll("left")}catch(a){setTimeout(la,1);return}c.ready()}}function Ma(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,i){var j=a.length;if(typeof b==="object"){for(var n in b)X(a,n,b[n],f,e,d);return a}if(d!==v){f=!i&&f&&c.isFunction(d);for(n=0;n<j;n++)e(a[n],b,f?d.call(a[n],n,e(a[n],b)):d,i);return a}return j?
-e(a[0],b):null}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function ma(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function na(a){var b,d=[],f=[],e=arguments,i,j,n,o,m,s,x=c.extend({},c.data(this,"events").live);if(!(a.button&&a.type==="click")){for(o in x){j=x[o];if(j.live===a.type||j.altLive&&c.inArray(a.type,j.altLive)>-1){i=j.data;i.beforeFilter&&i.beforeFilter[a.type]&&!i.beforeFilter[a.type](a)||f.push(j.selector)}else delete x[o]}i=c(a.target).closest(f,
-a.currentTarget);m=0;for(s=i.length;m<s;m++)for(o in x){j=x[o];n=i[m].elem;f=null;if(i[m].selector===j.selector){if(j.live==="mouseenter"||j.live==="mouseleave")f=c(a.relatedTarget).closest(j.selector)[0];if(!f||f!==n)d.push({elem:n,fn:j})}}m=0;for(s=d.length;m<s;m++){i=d[m];a.currentTarget=i.elem;a.data=i.fn.data;if(i.fn.apply(i.elem,e)===false){b=false;break}}return b}}function oa(a,b){return"live."+(a?a+".":"")+b.replace(/\./g,"`").replace(/ /g,"&")}function pa(a){return!a||!a.parentNode||a.parentNode.nodeType===
-11}function qa(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var i in f)for(var j in f[i])c.event.add(this,i,f[i][j],f[i][j].data)}}})}function ra(a,b,d){var f,e,i;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&a[0].indexOf("<option")<0&&(c.support.checkClone||!sa.test(a[0]))){e=true;if(i=c.fragments[a[0]])if(i!==1)f=i}if(!f){b=b&&b[0]?b[0].ownerDocument||b[0]:r;f=b.createDocumentFragment();
-c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=i?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(ta.concat.apply([],ta.slice(0,b)),function(){d[this]=a});return d}function ua(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Na=z.jQuery,Oa=z.$,r=z.document,S,Pa=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Qa=/^.[^:#\[\.,]*$/,Ra=/\S/,Sa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Ta=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,O=navigator.userAgent,
-va=false,P=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,Q=Array.prototype.slice,wa=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(typeof a==="string")if((d=Pa.exec(a))&&(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:r;if(a=Ta.exec(a))if(c.isPlainObject(b)){a=[r.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=ra([d[1]],
-[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}}else{if(b=r.getElementById(d[2])){if(b.id!==d[2])return S.find(a);this.length=1;this[0]=b}this.context=r;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=r;a=r.getElementsByTagName(a)}else return!b||b.jquery?(b||S).find(a):c(b).find(a);else if(c.isFunction(a))return S.ready(a);if(a.selector!==v){this.selector=a.selector;this.context=a.context}return c.isArray(a)?this.setArray(a):c.makeArray(a,
-this)},selector:"",jquery:"1.4.1",length:0,size:function(){return this.length},toArray:function(){return Q.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){a=c(a||null);a.prevObject=this;a.context=this.context;if(b==="find")a.selector=this.selector+(this.selector?" ":"")+d;else if(b)a.selector=this.selector+"."+b+"("+d+")";return a},setArray:function(a){this.length=0;ba.apply(this,a);return this},each:function(a,b){return c.each(this,
-a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(r,c);else P&&P.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(Q.apply(this,arguments),"slice",Q.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};
-c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,i,j,n;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(i in e){j=a[i];n=e[i];if(a!==n)if(f&&n&&(c.isPlainObject(n)||c.isArray(n))){j=j&&(c.isPlainObject(j)||c.isArray(j))?j:c.isArray(n)?[]:{};a[i]=c.extend(f,j,n)}else if(n!==v)a[i]=n}return a};c.extend({noConflict:function(a){z.$=
-Oa;if(a)z.jQuery=Na;return c},isReady:false,ready:function(){if(!c.isReady){if(!r.body)return setTimeout(c.ready,13);c.isReady=true;if(P){for(var a,b=0;a=P[b++];)a.call(r,c);P=null}c.fn.triggerHandler&&c(r).triggerHandler("ready")}},bindReady:function(){if(!va){va=true;if(r.readyState==="complete")return c.ready();if(r.addEventListener){r.addEventListener("DOMContentLoaded",L,false);z.addEventListener("load",c.ready,false)}else if(r.attachEvent){r.attachEvent("onreadystatechange",L);z.attachEvent("onload",
-c.ready);var a=false;try{a=z.frameElement==null}catch(b){}r.documentElement.doScroll&&a&&la()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype,"isPrototypeOf"))return false;var b;for(b in a);return b===v||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;
-return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return z.JSON&&z.JSON.parse?z.JSON.parse(a):(new Function("return "+a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Ra.test(a)){var b=r.getElementsByTagName("head")[0]||
-r.documentElement,d=r.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(r.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,i=a.length,j=i===v||c.isFunction(a);if(d)if(j)for(f in a){if(b.apply(a[f],d)===false)break}else for(;e<i;){if(b.apply(a[e++],d)===false)break}else if(j)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=
-a[0];e<i&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Sa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==
-v;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,i=a.length;e<i;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,i=0,j=a.length;i<j;i++){e=b(a[i],i,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=v}else if(b&&!c.isFunction(b)){d=b;b=v}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},
-uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});O=c.uaMatch(O);if(O.browser){c.browser[O.browser]=true;c.browser.version=O.version}if(c.browser.webkit)c.browser.safari=true;if(wa)c.inArray=function(a,b){return wa.call(b,a)};S=c(r);if(r.addEventListener)L=function(){r.removeEventListener("DOMContentLoaded",
-L,false);c.ready()};else if(r.attachEvent)L=function(){if(r.readyState==="complete"){r.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=r.documentElement,b=r.createElement("script"),d=r.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";var e=d.getElementsByTagName("*"),i=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!i)){c.support=
-{leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(i.getAttribute("style")),hrefNormalized:i.getAttribute("href")==="/a",opacity:/^0.55$/.test(i.style.opacity),cssFloat:!!i.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:r.createElement("select").appendChild(r.createElement("option")).selected,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};
-b.type="text/javascript";try{b.appendChild(r.createTextNode("window."+f+"=1;"))}catch(j){}a.insertBefore(b,a.firstChild);if(z[f]){c.support.scriptEval=true;delete z[f]}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function n(){c.support.noCloneEvent=false;d.detachEvent("onclick",n)});d.cloneNode(true).fireEvent("onclick")}d=r.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=r.createDocumentFragment();a.appendChild(d.firstChild);
-c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var n=r.createElement("div");n.style.width=n.style.paddingLeft="1px";r.body.appendChild(n);c.boxModel=c.support.boxModel=n.offsetWidth===2;r.body.removeChild(n).style.display="none"});a=function(n){var o=r.createElement("div");n="on"+n;var m=n in o;if(!m){o.setAttribute(n,"return;");m=typeof o[n]==="function"}return m};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=i=null}})();c.props=
-{"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ua=0,xa={},Va={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==z?xa:a;var f=a[G],e=c.cache;if(!b&&!f)return null;f||(f=++Ua);if(typeof b==="object"){a[G]=f;e=e[f]=c.extend(true,
-{},b)}else e=e[f]?e[f]:typeof d==="undefined"?Va:(e[f]={});if(d!==v){a[G]=f;e[b]=d}return typeof b==="string"?e[b]:e}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==z?xa:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{try{delete a[G]}catch(i){a.removeAttribute&&a.removeAttribute(G)}delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,
-a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===v){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===v&&this.length)f=c.data(this[0],a);return f===v&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);
-return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===v)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||
-a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var ya=/[\n\t]/g,ca=/\s+/,Wa=/\r/g,Xa=/href|src|style/,Ya=/(button|input)/i,Za=/(button|input|object|select|textarea)/i,$a=/^(a|area)$/i,za=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(o){var m=
-c(this);m.addClass(a.call(this,o,m.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className)for(var i=" "+e.className+" ",j=0,n=b.length;j<n;j++){if(i.indexOf(" "+b[j]+" ")<0)e.className+=" "+b[j]}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(o){var m=c(this);m.removeClass(a.call(this,o,m.attr("class")))});if(a&&typeof a==="string"||a===v)for(var b=(a||"").split(ca),
-d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var i=(" "+e.className+" ").replace(ya," "),j=0,n=b.length;j<n;j++)i=i.replace(" "+b[j]+" "," ");e.className=i.substring(1,i.length-1)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var i=c(this);i.toggleClass(a.call(this,e,i.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,i=0,j=c(this),n=b,o=
-a.split(ca);e=o[i++];){n=f?n:!j.hasClass(e);j[n?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(ya," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===v){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||
-{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var i=b?d:0;for(d=b?d+1:e.length;i<d;i++){var j=e[i];if(j.selected){a=c(j).val();if(b)return a;f.push(a)}}return f}if(za.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Wa,"")}return v}var n=c.isFunction(a);return this.each(function(o){var m=c(this),s=a;if(this.nodeType===1){if(n)s=a.call(this,o,m.val());
-if(typeof s==="number")s+="";if(c.isArray(s)&&za.test(this.type))this.checked=c.inArray(m.val(),s)>=0;else if(c.nodeName(this,"select")){var x=c.makeArray(s);c("option",this).each(function(){this.selected=c.inArray(c(this).val(),x)>=0});if(!x.length)this.selectedIndex=-1}else this.value=s}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return v;if(f&&b in c.attrFn)return c(a)[b](d);
-f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==v;b=f&&c.props[b]||b;if(a.nodeType===1){var i=Xa.test(b);if(b in a&&f&&!i){if(e){b==="type"&&Ya.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:Za.test(a.nodeName)||$a.test(a.nodeName)&&a.href?0:v;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=
-""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&i?a.getAttribute(b,2):a.getAttribute(b);return a===null?v:a}return c.style(a,b,d)}});var ab=function(a){return a.replace(/[^\w\s\.\|`]/g,function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==z&&!a.frameElement)a=z;if(!d.guid)d.guid=c.guid++;if(f!==v){d=c.proxy(d);d.data=f}var e=c.data(a,"events")||c.data(a,"events",{}),i=c.data(a,"handle"),j;if(!i){j=
-function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(j.elem,arguments):v};i=c.data(a,"handle",j)}if(i){i.elem=a;b=b.split(/\s+/);for(var n,o=0;n=b[o++];){var m=n.split(".");n=m.shift();if(o>1){d=c.proxy(d);if(f!==v)d.data=f}d.type=m.slice(0).sort().join(".");var s=e[n],x=this.special[n]||{};if(!s){s=e[n]={};if(!x.setup||x.setup.call(a,f,m,d)===false)if(a.addEventListener)a.addEventListener(n,i,false);else a.attachEvent&&a.attachEvent("on"+n,i)}if(x.add)if((m=x.add.call(a,
-d,f,m,s))&&c.isFunction(m)){m.guid=m.guid||d.guid;m.data=m.data||d.data;m.type=m.type||d.type;d=m}s[d.guid]=d;this.global[n]=true}a=null}}},global:{},remove:function(a,b,d){if(!(a.nodeType===3||a.nodeType===8)){var f=c.data(a,"events"),e,i,j;if(f){if(b===v||typeof b==="string"&&b.charAt(0)===".")for(i in f)this.remove(a,i+(b||""));else{if(b.type){d=b.handler;b=b.type}b=b.split(/\s+/);for(var n=0;i=b[n++];){var o=i.split(".");i=o.shift();var m=!o.length,s=c.map(o.slice(0).sort(),ab);s=new RegExp("(^|\\.)"+
-s.join("\\.(?:.*\\.)?")+"(\\.|$)");var x=this.special[i]||{};if(f[i]){if(d){j=f[i][d.guid];delete f[i][d.guid]}else for(var A in f[i])if(m||s.test(f[i][A].type))delete f[i][A];x.remove&&x.remove.call(a,o,j);for(e in f[i])break;if(!e){if(!x.teardown||x.teardown.call(a,o)===false)if(a.removeEventListener)a.removeEventListener(i,c.data(a,"handle"),false);else a.detachEvent&&a.detachEvent("on"+i,c.data(a,"handle"));e=null;delete f[i]}}}}for(e in f)break;if(!e){if(A=c.data(a,"handle"))A.elem=null;c.removeData(a,
-"events");c.removeData(a,"handle")}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();this.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return v;a.result=v;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,
-b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(i){}if(!a.isPropagationStopped()&&f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){d=a.target;var j;if(!(c.nodeName(d,"a")&&e==="click")&&!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()])){try{if(d[e]){if(j=d["on"+e])d["on"+e]=null;this.triggered=true;d[e]()}}catch(n){}if(j)d["on"+e]=j;this.triggered=false}}},handle:function(a){var b,
-d;a=arguments[0]=c.event.fix(a||z.event);a.currentTarget=this;d=a.type.split(".");a.type=d.shift();b=!d.length&&!a.exclusive;var f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)");d=(c.data(this,"events")||{})[a.type];for(var e in d){var i=d[e];if(b||f.test(i.type)){a.handler=i;a.data=i.data;i=i.apply(this,arguments);if(i!==v){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
-fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||r;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=r.documentElement;d=r.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
-d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==v)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a,b){c.extend(a,b||{});a.guid+=b.selector+b.live;b.liveProxy=a;c.event.add(this,b.live,na,b)},remove:function(a){if(a.length){var b=
-0,d=new RegExp("(^|\\.)"+a[0]+"(\\.|$)");c.each(c.data(this,"events").live||{},function(){d.test(this.type)&&b++});b<1&&c.event.remove(this,a[0],na)}},special:{}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};
-c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,isImmediatePropagationStopped:Y};var Aa=function(a){for(var b=
-a.relatedTarget;b&&b!==this;)try{b=b.parentNode}catch(d){break}if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}},Ba=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ba:Aa,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ba:Aa)}}});if(!c.support.submitBubbles)c.event.special.submit={setup:function(a,b,d){if(this.nodeName.toLowerCase()!==
-"form"){c.event.add(this,"click.specialSubmit."+d.guid,function(f){var e=f.target,i=e.type;if((i==="submit"||i==="image")&&c(e).closest("form").length)return ma("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit."+d.guid,function(f){var e=f.target,i=e.type;if((i==="text"||i==="password")&&c(e).closest("form").length&&f.keyCode===13)return ma("submit",this,arguments)})}else return false},remove:function(a,b){c.event.remove(this,"click.specialSubmit"+(b?"."+b.guid:""));c.event.remove(this,
-"keypress.specialSubmit"+(b?"."+b.guid:""))}};if(!c.support.changeBubbles){var da=/textarea|input|select/i;function Ca(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d}function ea(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Ca(d);if(a.type!=="focusout"||
-d.type!=="radio")c.data(d,"_change_data",e);if(!(f===v||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}}c.event.special.change={filters:{focusout:ea,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return ea.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return ea.call(this,a)},beforeactivate:function(a){a=
-a.target;a.nodeName.toLowerCase()==="input"&&a.type==="radio"&&c.data(a,"_change_data",Ca(a))}},setup:function(a,b,d){for(var f in T)c.event.add(this,f+".specialChange."+d.guid,T[f]);return da.test(this.nodeName)},remove:function(a,b){for(var d in T)c.event.remove(this,d+".specialChange"+(b?"."+b.guid:""),T[d]);return da.test(this.nodeName)}};var T=c.event.special.change.filters}r.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,
-f)}c.event.special[b]={setup:function(){this.addEventListener(a,d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var i in d)this[b](i,f,d[i],e);return this}if(c.isFunction(f)){e=f;f=v}var j=b==="one"?c.proxy(e,function(n){c(this).unbind(n,j);return e.apply(this,arguments)}):e;return d==="unload"&&b!=="one"?this.one(d,f,e):this.each(function(){c.event.add(this,d,j,f)})}});c.fn.extend({unbind:function(a,
-b){if(typeof a==="object"&&!a.preventDefault){for(var d in a)this.unbind(d,a[d]);return this}return this.each(function(){c.event.remove(this,a,b)})},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+
-a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e){var i,j=0;if(c.isFunction(f)){e=f;f=v}for(d=(d||"").split(/\s+/);(i=d[j++])!=null;){i=i==="focus"?"focusin":i==="blur"?"focusout":i==="hover"?d.push("mouseleave")&&"mouseenter":i;b==="live"?c(this.context).bind(oa(i,this.selector),{data:f,selector:this.selector,
-live:i},e):c(this.context).unbind(oa(i,this.selector),e?{guid:e.guid+this.selector+i}:null)}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});z.attachEvent&&!z.addEventListener&&z.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});
-(function(){function a(g){for(var h="",k,l=0;g[l];l++){k=g[l];if(k.nodeType===3||k.nodeType===4)h+=k.nodeValue;else if(k.nodeType!==8)h+=a(k.childNodes)}return h}function b(g,h,k,l,q,p){q=0;for(var u=l.length;q<u;q++){var t=l[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===k){y=l[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=k;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}l[q]=y}}}function d(g,h,k,l,q,p){q=0;for(var u=l.length;q<u;q++){var t=l[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===
-k){y=l[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=k;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(o.filter(h,[t]).length>0){y=t;break}}t=t[g]}l[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,i=Object.prototype.toString,j=false,n=true;[0,0].sort(function(){n=false;return 0});var o=function(g,h,k,l){k=k||[];var q=h=h||r;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||
-typeof g!=="string")return k;for(var p=[],u,t,y,R,H=true,M=w(h),I=g;(f.exec(""),u=f.exec(I))!==null;){I=u[3];p.push(u[1]);if(u[2]){R=u[3];break}}if(p.length>1&&s.exec(g))if(p.length===2&&m.relative[p[0]])t=fa(p[0]+p[1],h);else for(t=m.relative[p[0]]?[h]:o(p.shift(),h);p.length;){g=p.shift();if(m.relative[g])g+=p.shift();t=fa(g,t)}else{if(!l&&p.length>1&&h.nodeType===9&&!M&&m.match.ID.test(p[0])&&!m.match.ID.test(p[p.length-1])){u=o.find(p.shift(),h,M);h=u.expr?o.filter(u.expr,u.set)[0]:u.set[0]}if(h){u=
-l?{expr:p.pop(),set:A(l)}:o.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=u.expr?o.filter(u.expr,u.set):u.set;if(p.length>0)y=A(t);else H=false;for(;p.length;){var D=p.pop();u=D;if(m.relative[D])u=p.pop();else D="";if(u==null)u=h;m.relative[D](y,u,M)}}else y=[]}y||(y=t);y||o.error(D||g);if(i.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))k.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&
-y[g].nodeType===1&&k.push(t[g]);else k.push.apply(k,y);else A(y,k);if(R){o(R,q,k,l);o.uniqueSort(k)}return k};o.uniqueSort=function(g){if(C){j=n;g.sort(C);if(j)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};o.matches=function(g,h){return o(g,null,null,h)};o.find=function(g,h,k){var l,q;if(!g)return[];for(var p=0,u=m.order.length;p<u;p++){var t=m.order[p];if(q=m.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");l=m.find[t](q,
-h,k);if(l!=null){g=g.replace(m.match[t],"");break}}}}l||(l=h.getElementsByTagName("*"));return{set:l,expr:g}};o.filter=function(g,h,k,l){for(var q=g,p=[],u=h,t,y,R=h&&h[0]&&w(h[0]);g&&h.length;){for(var H in m.filter)if((t=m.leftMatch[H].exec(g))!=null&&t[2]){var M=m.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-1)!=="\\"){if(u===p)p=[];if(m.preFilter[H])if(t=m.preFilter[H](t,u,k,p,l,R)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=u[U])!=null;U++)if(D){I=M(D,t,U,u);var Da=
-l^!!I;if(k&&I!=null)if(Da)y=true;else u[U]=false;else if(Da){p.push(D);y=true}}if(I!==v){k||(u=p);g=g.replace(m.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)o.error(g);else break;q=g}return u};o.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var m=o.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
-TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},relative:{"+":function(g,h){var k=typeof h==="string",l=k&&!/\W/.test(h);k=k&&!l;if(l)h=h.toLowerCase();l=0;for(var q=g.length,
-p;l<q;l++)if(p=g[l]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[l]=k||p&&p.nodeName.toLowerCase()===h?p||false:p===h}k&&o.filter(h,g,true)},">":function(g,h){var k=typeof h==="string";if(k&&!/\W/.test(h)){h=h.toLowerCase();for(var l=0,q=g.length;l<q;l++){var p=g[l];if(p){k=p.parentNode;g[l]=k.nodeName.toLowerCase()===h?k:false}}}else{l=0;for(q=g.length;l<q;l++)if(p=g[l])g[l]=k?p.parentNode:p.parentNode===h;k&&o.filter(h,g,true)}},"":function(g,h,k){var l=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=
-h=h.toLowerCase();q=b}q("parentNode",h,l,g,p,k)},"~":function(g,h,k){var l=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,l,g,p,k)}},find:{ID:function(g,h,k){if(typeof h.getElementById!=="undefined"&&!k)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var k=[];h=h.getElementsByName(g[1]);for(var l=0,q=h.length;l<q;l++)h[l].getAttribute("name")===g[1]&&k.push(h[l]);return k.length===0?null:k}},
-TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,k,l,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var u;(u=h[p])!=null;p++)if(u)if(q^(u.className&&(" "+u.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))k||l.push(u);else if(k)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&
-"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,k,l,q,p){h=g[1].replace(/\\/g,"");if(!p&&m.attrMap[h])g[1]=m.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,k,l,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=o(g[3],null,null,h);else{g=o.filter(g[3],h,k,true^q);k||l.push.apply(l,g);return false}else if(m.match.POS.test(g[0])||m.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);
-return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,k){return!!o(k[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===
-g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},setFilters:{first:function(g,h){return h===0},last:function(g,h,k,l){return h===l.length-1},even:function(g,h){return h%2===
-0},odd:function(g,h){return h%2===1},lt:function(g,h,k){return h<k[3]-0},gt:function(g,h,k){return h>k[3]-0},nth:function(g,h,k){return k[3]-0===h},eq:function(g,h,k){return k[3]-0===h}},filter:{PSEUDO:function(g,h,k,l){var q=h[1],p=m.filters[q];if(p)return p(g,k,h,l);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=h[3];k=0;for(l=h.length;k<l;k++)if(h[k]===g)return false;return true}else o.error("Syntax error, unrecognized expression: "+
-q)},CHILD:function(g,h){var k=h[1],l=g;switch(k){case "only":case "first":for(;l=l.previousSibling;)if(l.nodeType===1)return false;if(k==="first")return true;l=g;case "last":for(;l=l.nextSibling;)if(l.nodeType===1)return false;return true;case "nth":k=h[2];var q=h[3];if(k===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var u=0;for(l=p.firstChild;l;l=l.nextSibling)if(l.nodeType===1)l.nodeIndex=++u;p.sizcache=h}g=g.nodeIndex-q;return k===0?g===0:g%k===0&&g/k>=
-0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var k=h[1];g=m.attrHandle[k]?m.attrHandle[k](g):g[k]!=null?g[k]:g.getAttribute(k);k=g+"";var l=h[2];h=h[4];return g==null?l==="!=":l==="="?k===h:l==="*="?k.indexOf(h)>=0:l==="~="?(" "+k+" ").indexOf(h)>=0:!h?k&&g!==false:l==="!="?k!==h:l==="^="?
-k.indexOf(h)===0:l==="$="?k.substr(k.length-h.length)===h:l==="|="?k===h||k.substr(0,h.length+1)===h+"-":false},POS:function(g,h,k,l){var q=m.setFilters[h[2]];if(q)return q(g,k,h,l)}}},s=m.match.POS;for(var x in m.match){m.match[x]=new RegExp(m.match[x].source+/(?![^\[]*\])(?![^\(]*\))/.source);m.leftMatch[x]=new RegExp(/(^(?:.|\r|\n)*?)/.source+m.match[x].source.replace(/\\(\d+)/g,function(g,h){return"\\"+(h-0+1)}))}var A=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};
-try{Array.prototype.slice.call(r.documentElement.childNodes,0)}catch(B){A=function(g,h){h=h||[];if(i.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var k=0,l=g.length;k<l;k++)h.push(g[k]);else for(k=0;g[k];k++)h.push(g[k]);return h}}var C;if(r.documentElement.compareDocumentPosition)C=function(g,h){if(!g.compareDocumentPosition||!h.compareDocumentPosition){if(g==h)j=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===
-h?0:1;if(g===0)j=true;return g};else if("sourceIndex"in r.documentElement)C=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)j=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)j=true;return g};else if(r.createRange)C=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)j=true;return g.ownerDocument?-1:1}var k=g.ownerDocument.createRange(),l=h.ownerDocument.createRange();k.setStart(g,0);k.setEnd(g,0);l.setStart(h,0);l.setEnd(h,0);g=k.compareBoundaryPoints(Range.START_TO_END,
-l);if(g===0)j=true;return g};(function(){var g=r.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var k=r.documentElement;k.insertBefore(g,k.firstChild);if(r.getElementById(h)){m.find.ID=function(l,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(l[1]))?q.id===l[1]||typeof q.getAttributeNode!=="undefined"&&q.getAttributeNode("id").nodeValue===l[1]?[q]:v:[]};m.filter.ID=function(l,q){var p=typeof l.getAttributeNode!=="undefined"&&l.getAttributeNode("id");
-return l.nodeType===1&&p&&p.nodeValue===q}}k.removeChild(g);k=g=null})();(function(){var g=r.createElement("div");g.appendChild(r.createComment(""));if(g.getElementsByTagName("*").length>0)m.find.TAG=function(h,k){k=k.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var l=0;k[l];l++)k[l].nodeType===1&&h.push(k[l]);k=h}return k};g.innerHTML="<a href='#'></a>";if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")m.attrHandle.href=function(h){return h.getAttribute("href",
-2)};g=null})();r.querySelectorAll&&function(){var g=o,h=r.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){o=function(l,q,p,u){q=q||r;if(!u&&q.nodeType===9&&!w(q))try{return A(q.querySelectorAll(l),p)}catch(t){}return g(l,q,p,u)};for(var k in g)o[k]=g[k];h=null}}();(function(){var g=r.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===
-0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){m.order.splice(1,0,"CLASS");m.find.CLASS=function(h,k,l){if(typeof k.getElementsByClassName!=="undefined"&&!l)return k.getElementsByClassName(h[1])};g=null}}})();var E=r.compareDocumentPosition?function(g,h){return g.compareDocumentPosition(h)&16}:function(g,h){return g!==h&&(g.contains?g.contains(h):true)},w=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},fa=function(g,h){var k=[],
-l="",q;for(h=h.nodeType?[h]:h;q=m.match.PSEUDO.exec(g);){l+=q[0];g=g.replace(m.match.PSEUDO,"")}g=m.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)o(g,h[q],k);return o.filter(l,k)};c.find=o;c.expr=o.selectors;c.expr[":"]=c.expr.filters;c.unique=o.uniqueSort;c.getText=a;c.isXMLDoc=w;c.contains=E})();var bb=/Until$/,cb=/^(?:parents|prevUntil|prevAll)/,db=/,/;Q=Array.prototype.slice;var Ea=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,i){return!!b.call(e,i,e)===d});else if(b.nodeType)return c.grep(a,
-function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Qa.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;c.find(a,this[f],b);if(f>0)for(var i=d;i<b.length;i++)for(var j=0;j<d;j++)if(b[j]===b[i]){b.splice(i--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=
-0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ea(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ea(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,i={},j;if(f&&a.length){e=0;for(var n=a.length;e<n;e++){j=a[e];i[j]||(i[j]=c.expr.match.POS.test(j)?c(j,b||this.context):j)}for(;f&&f.ownerDocument&&f!==b;){for(j in i){e=i[j];if(e.jquery?e.index(f)>
--1:c(f).is(e)){d.push({selector:j,elem:f});delete i[j]}}f=f.parentNode}}return d}var o=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(m,s){for(;s&&s.ownerDocument&&s!==b;){if(o?o.index(s)>-1:c(s).is(a))return s;s=s.parentNode}return null})},index:function(a){if(!a||typeof a==="string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),
-a);return this.pushStack(pa(a[0])||pa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},
-nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);bb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):
-e;if((this.length>1||db.test(f))&&cb.test(a))e=e.reverse();return this.pushStack(e,a,Q.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===v||a.nodeType!==1||!c(a).is(d));){a.nodeType===1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==
-b&&d.push(a);return d}});var Fa=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ga=/(<([\w:]+)[^>]*?)\/>/g,eb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,Ha=/<([\w:]+)/,fb=/<tbody/i,gb=/<|&\w+;/,sa=/checked\s*(?:[^=]|=\s*.checked.)/i,Ia=function(a,b,d){return eb.test(d)?a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],
-col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==v)return this.empty().append((this[0]&&this[0].ownerDocument||r).createTextNode(a));return c.getText(this)},
-wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?
-d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,
-false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&
-!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Fa,"").replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){qa(this,b);qa(this.find("*"),b.find("*"))}return b},html:function(a){if(a===v)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Fa,""):null;else if(typeof a==="string"&&!/<script/i.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(Ha.exec(a)||
-["",""])[1].toLowerCase()]){a=a.replace(Ga,Ia);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var i=c(this),j=i.html();i.empty().append(function(){return a.call(this,e,j)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,
-b,f))});else a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(s){return c.nodeName(s,"table")?s.getElementsByTagName("tbody")[0]||s.appendChild(s.ownerDocument.createElement("tbody")):s}var e,i,j=a[0],n=[];if(!c.support.checkClone&&arguments.length===3&&typeof j===
-"string"&&sa.test(j))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(j))return this.each(function(s){var x=c(this);a[0]=j.call(this,s,b?x.html():v);x.domManip(a,b,d)});if(this[0]){e=a[0]&&a[0].parentNode&&a[0].parentNode.nodeType===11?{fragment:a[0].parentNode}:ra(a,this,n);if(i=e.fragment.firstChild){b=b&&c.nodeName(i,"tr");for(var o=0,m=this.length;o<m;o++)d.call(b?f(this[o],i):this[o],e.cacheable||this.length>1||o>0?e.fragment.cloneNode(true):e.fragment)}n&&c.each(n,
-Ma)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);for(var e=0,i=d.length;e<i;e++){var j=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),j);f=f.concat(j)}return this.pushStack(f,a,d.selector)}});c.each({remove:function(a,b){if(!a||c.filter(a,[this]).length){if(!b&&this.nodeType===1){c.cleanData(this.getElementsByTagName("*"));c.cleanData([this])}this.parentNode&&
-this.parentNode.removeChild(this)}},empty:function(){for(this.nodeType===1&&c.cleanData(this.getElementsByTagName("*"));this.firstChild;)this.removeChild(this.firstChild)}},function(a,b){c.fn[a]=function(){return this.each(b,arguments)}});c.extend({clean:function(a,b,d,f){b=b||r;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||r;var e=[];c.each(a,function(i,j){if(typeof j==="number")j+="";if(j){if(typeof j==="string"&&!gb.test(j))j=b.createTextNode(j);else if(typeof j===
-"string"){j=j.replace(Ga,Ia);var n=(Ha.exec(j)||["",""])[1].toLowerCase(),o=F[n]||F._default,m=o[0];i=b.createElement("div");for(i.innerHTML=o[1]+j+o[2];m--;)i=i.lastChild;if(!c.support.tbody){m=fb.test(j);n=n==="table"&&!m?i.firstChild&&i.firstChild.childNodes:o[1]==="<table>"&&!m?i.childNodes:[];for(o=n.length-1;o>=0;--o)c.nodeName(n[o],"tbody")&&!n[o].childNodes.length&&n[o].parentNode.removeChild(n[o])}!c.support.leadingWhitespace&&V.test(j)&&i.insertBefore(b.createTextNode(V.exec(j)[0]),i.firstChild);
-j=c.makeArray(i.childNodes)}if(j.nodeType)e.push(j);else e=c.merge(e,j)}});if(d)for(a=0;e[a];a++)if(f&&c.nodeName(e[a],"script")&&(!e[a].type||e[a].type.toLowerCase()==="text/javascript"))f.push(e[a].parentNode?e[a].parentNode.removeChild(e[a]):e[a]);else{e[a].nodeType===1&&e.splice.apply(e,[a+1,0].concat(c.makeArray(e[a].getElementsByTagName("script"))));d.appendChild(e[a])}return e},cleanData:function(a){for(var b=0,d;(d=a[b])!=null;b++){c.event.remove(d);c.removeData(d)}}});var hb=/z-?index|font-?weight|opacity|zoom|line-?height/i,
-Ja=/alpha\([^)]*\)/,Ka=/opacity=([^)]*)/,ga=/float/i,ha=/-([a-z])/ig,ib=/([A-Z])/g,jb=/^-?\d+(?:px)?$/i,kb=/^-?\d/,lb={position:"absolute",visibility:"hidden",display:"block"},mb=["Left","Right"],nb=["Top","Bottom"],ob=r.defaultView&&r.defaultView.getComputedStyle,La=c.support.cssFloat?"cssFloat":"styleFloat",ia=function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===v)return c.curCSS(d,f);if(typeof e==="number"&&!hb.test(f))e+="px";c.style(d,f,e)})};
-c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return v;if((b==="width"||b==="height")&&parseFloat(d)<0)d=v;var f=a.style||a,e=d!==v;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=Ja.test(a)?a.replace(Ja,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Ka.exec(f.filter)[1])/100+"":""}if(ga.test(b))b=La;b=b.replace(ha,ia);if(e)f[b]=d;return f[b]},css:function(a,
-b,d,f){if(b==="width"||b==="height"){var e,i=b==="width"?mb:nb;function j(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(i,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,"border"+this+"Width",true))||0})}a.offsetWidth!==0?j():c.swap(a,lb,j);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&
-a.currentStyle){f=Ka.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ga.test(b))b=La;if(!d&&e&&e[b])f=e[b];else if(ob){if(ga.test(b))b="float";b=b.replace(ib,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ha,ia);f=a.currentStyle[b]||a.currentStyle[d];if(!jb.test(f)&&kb.test(f)){b=e.left;var i=a.runtimeStyle.left;a.runtimeStyle.left=
-a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=i}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var pb=
-J(),qb=/<script(.|\s)*?\/script>/gi,rb=/select|textarea/i,sb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ja=/\?/,tb=/(\?|&)_=.*?(&|$)/,ub=/^(\w+:)?\/\/([^\/?#]+)/,vb=/%20/g;c.fn.extend({_load:c.fn.load,load:function(a,b,d){if(typeof a!=="string")return this._load(a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=
-c.param(b,c.ajaxSettings.traditional);f="POST"}var i=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(j,n){if(n==="success"||n==="notmodified")i.html(e?c("<div />").append(j.responseText.replace(qb,"")).find(e):j.responseText);d&&i.each(d,[j.responseText,n,j])}});return this},serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&
-(this.checked||rb.test(this.nodeName)||sb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,
-b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:z.XMLHttpRequest&&(z.location.protocol!=="file:"||!z.ActiveXObject)?function(){return new z.XMLHttpRequest}:
-function(){try{return new z.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&e.success.call(o,n,j,w);e.global&&f("ajaxSuccess",[w,e])}function d(){e.complete&&e.complete.call(o,w,j);e.global&&f("ajaxComplete",[w,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}
-function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),i,j,n,o=a&&a.context||e,m=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(m==="GET")N.test(e.url)||(e.url+=(ja.test(e.url)?"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||
-N.test(e.url))){i=e.jsonpCallback||"jsonp"+pb++;if(e.data)e.data=(e.data+"").replace(N,"="+i+"$1");e.url=e.url.replace(N,"="+i+"$1");e.dataType="script";z[i]=z[i]||function(q){n=q;b();d();z[i]=v;try{delete z[i]}catch(p){}A&&A.removeChild(B)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===false&&m==="GET"){var s=J(),x=e.url.replace(tb,"$1_="+s+"$2");e.url=x+(x===e.url?(ja.test(e.url)?"&":"?")+"_="+s:"")}if(e.data&&m==="GET")e.url+=(ja.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&
-c.event.trigger("ajaxStart");s=(s=ub.exec(e.url))&&(s[1]&&s[1]!==location.protocol||s[2]!==location.host);if(e.dataType==="script"&&m==="GET"&&s){var A=r.getElementsByTagName("head")[0]||r.documentElement,B=r.createElement("script");B.src=e.url;if(e.scriptCharset)B.charset=e.scriptCharset;if(!i){var C=false;B.onload=B.onreadystatechange=function(){if(!C&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){C=true;b();d();B.onload=B.onreadystatechange=null;A&&B.parentNode&&
-A.removeChild(B)}}}A.insertBefore(B,A.firstChild);return v}var E=false,w=e.xhr();if(w){e.username?w.open(m,e.url,e.async,e.username,e.password):w.open(m,e.url,e.async);try{if(e.data||a&&a.contentType)w.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&w.setRequestHeader("If-Modified-Since",c.lastModified[e.url]);c.etag[e.url]&&w.setRequestHeader("If-None-Match",c.etag[e.url])}s||w.setRequestHeader("X-Requested-With","XMLHttpRequest");w.setRequestHeader("Accept",
-e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(fa){}if(e.beforeSend&&e.beforeSend.call(o,w,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");w.abort();return false}e.global&&f("ajaxSend",[w,e]);var g=w.onreadystatechange=function(q){if(!w||w.readyState===0||q==="abort"){E||d();E=true;if(w)w.onreadystatechange=c.noop}else if(!E&&w&&(w.readyState===4||q==="timeout")){E=true;w.onreadystatechange=c.noop;j=q==="timeout"?"timeout":!c.httpSuccess(w)?
-"error":e.ifModified&&c.httpNotModified(w,e.url)?"notmodified":"success";var p;if(j==="success")try{n=c.httpData(w,e.dataType,e)}catch(u){j="parsererror";p=u}if(j==="success"||j==="notmodified")i||b();else c.handleError(e,w,j,p);d();q==="timeout"&&w.abort();if(e.async)w=null}};try{var h=w.abort;w.abort=function(){w&&h.call(w);g("abort")}}catch(k){}e.async&&e.timeout>0&&setTimeout(function(){w&&!E&&g("timeout")},e.timeout);try{w.send(m==="POST"||m==="PUT"||m==="DELETE"?e.data:null)}catch(l){c.handleError(e,
-w,null,l);d()}e.async||g();return w}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=
-f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b==="json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(j,n){if(c.isArray(n))c.each(n,
-function(o,m){b?f(j,m):d(j+"["+(typeof m==="object"||c.isArray(m)?o:"")+"]",m)});else!b&&n!=null&&typeof n==="object"?c.each(n,function(o,m){d(j+"["+o+"]",m)}):f(j,n)}function f(j,n){n=c.isFunction(n)?n():n;e[e.length]=encodeURIComponent(j)+"="+encodeURIComponent(n)}var e=[];if(b===v)b=c.ajaxSettings.traditional;if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var i in a)d(i,a[i]);return e.join("&").replace(vb,"+")}});var ka={},wb=/toggle|show|hide/,xb=/^([+-]=)?([\d+-.]+)(.*)$/,
-W,ta=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(ka[d])f=ka[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();
-ka[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&
-c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var i=c.extend({},e),j,n=this.nodeType===1&&c(this).is(":hidden"),
-o=this;for(j in a){var m=j.replace(ha,ia);if(j!==m){a[m]=a[j];delete a[j];j=m}if(a[j]==="hide"&&n||a[j]==="show"&&!n)return i.complete.call(this);if((j==="height"||j==="width")&&this.style){i.display=c.css(this,"display");i.overflow=this.style.overflow}if(c.isArray(a[j])){(i.specialEasing=i.specialEasing||{})[j]=a[j][1];a[j]=a[j][0]}}if(i.overflow!=null)this.style.overflow="hidden";i.curAnim=c.extend({},a);c.each(a,function(s,x){var A=new c.fx(o,i,s);if(wb.test(x))A[x==="toggle"?n?"show":"hide":x](a);
-else{var B=xb.exec(x),C=A.cur(true)||0;if(B){x=parseFloat(B[2]);var E=B[3]||"px";if(E!=="px"){o.style[s]=(x||1)+E;C=(x||1)/A.cur(true)*C;o.style[s]=C+E}if(B[1])x=(B[1]==="-="?-1:1)*x+C;A.custom(C,x,E)}else A.custom(C,x,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",
-1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration==="number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,
-b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==
-null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(i){return e.step(i)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop===
-"width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=
-this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=
-c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=
-null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in r.documentElement?function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),
-f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=this[0];if(a)return this.each(function(s){c.offset.setOffset(this,a,s)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=
-b,e=b.ownerDocument,i,j=e.documentElement,n=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var o=b.offsetTop,m=b.offsetLeft;(b=b.parentNode)&&b!==n&&b!==j;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;i=e?e.getComputedStyle(b,null):b.currentStyle;o-=b.scrollTop;m-=b.scrollLeft;if(b===d){o+=b.offsetTop;m+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){o+=parseFloat(i.borderTopWidth)||
-0;m+=parseFloat(i.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&i.overflow!=="visible"){o+=parseFloat(i.borderTopWidth)||0;m+=parseFloat(i.borderLeftWidth)||0}f=i}if(f.position==="relative"||f.position==="static"){o+=n.offsetTop;m+=n.offsetLeft}if(c.offset.supportsFixedPosition&&f.position==="fixed"){o+=Math.max(j.scrollTop,n.scrollTop);m+=Math.max(j.scrollLeft,n.scrollLeft)}return{top:o,left:m}};c.offset={initialize:function(){var a=r.body,b=r.createElement("div"),
-d,f,e,i=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";a.insertBefore(b,a.firstChild);
-d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i;a.removeChild(b);c.offset.initialize=c.noop},
-bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),i=parseInt(c.curCSS(a,"top",true),10)||0,j=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,d,e);d={top:b.top-e.top+i,left:b.left-
-e.left+j};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=
-this.offsetParent||r.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],i;if(!e)return null;if(f!==v)return this.each(function(){if(i=ua(this))i.scrollTo(!a?f:c(i).scrollLeft(),a?f:c(i).scrollTop());else this[d]=f});else return(i=ua(e))?"pageXOffset"in i?i[a?"pageYOffset":"pageXOffset"]:c.support.boxModel&&i.document.documentElement[d]||i.document.body[d]:e[d]}});
-c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(i){var j=c(this);j[d](f.call(this,i,j[d]()))});return"scrollTo"in e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||
-e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===v?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});z.jQuery=z.$=c})(window);
+(function(){
+
+var
+ // Will speed up references to window, and allows munging its name.
+ window = this,
+ // Will speed up references to undefined, and allows munging its name.
+ undefined,
+ // Map over jQuery in case of overwrite
+ _jQuery = window.jQuery,
+ // Map over the $ in case of overwrite
+ _$ = window.$,
+
+ jQuery = window.jQuery = window.$ = function( selector, context ) {
+ // The jQuery object is actually just the init constructor 'enhanced'
+ return new jQuery.fn.init( selector, context );
+ },
+
+ // A simple way to check for HTML strings or ID strings
+ // (both of which we optimize for)
+ quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,
+ // Is it a simple selector
+ isSimple = /^.[^:#\[\.,]*$/;
+
+jQuery.fn = jQuery.prototype = {
+ init: function( selector, context ) {
+ // Make sure that a selection was provided
+ selector = selector || document;
+
+ // Handle $(DOMElement)
+ if ( selector.nodeType ) {
+ this[0] = selector;
+ this.length = 1;
+ this.context = selector;
+ return this;
+ }
+ // Handle HTML strings
+ if ( typeof selector === "string" ) {
+ // Are we dealing with HTML string or an ID?
+ var match = quickExpr.exec( selector );
+
+ // Verify a match, and that no context was specified for #id
+ if ( match && (match[1] || !context) ) {
+
+ // HANDLE: $(html) -> $(array)
+ if ( match[1] )
+ selector = jQuery.clean( [ match[1] ], context );
+
+ // HANDLE: $("#id")
+ else {
+ var elem = document.getElementById( match[3] );
+
+ // Handle the case where IE and Opera return items
+ // by name instead of ID
+ if ( elem && elem.id != match[3] )
+ return jQuery().find( selector );
+
+ // Otherwise, we inject the element directly into the jQuery object
+ var ret = jQuery( elem || [] );
+ ret.context = document;
+ ret.selector = selector;
+ return ret;
+ }
+
+ // HANDLE: $(expr, [context])
+ // (which is just equivalent to: $(content).find(expr)
+ } else
+ return jQuery( context ).find( selector );
+
+ // HANDLE: $(function)
+ // Shortcut for document ready
+ } else if ( jQuery.isFunction( selector ) )
+ return jQuery( document ).ready( selector );
+
+ // Make sure that old selector state is passed along
+ if ( selector.selector && selector.context ) {
+ this.selector = selector.selector;
+ this.context = selector.context;
+ }
+
+ return this.setArray(jQuery.isArray( selector ) ?
+ selector :
+ jQuery.makeArray(selector));
+ },
+
+ // Start with an empty selector
+ selector: "",
+
+ // The current version of jQuery being used
+ jquery: "1.3.2",
+
+ // The number of elements contained in the matched element set
+ size: function() {
+ return this.length;
+ },
+
+ // Get the Nth element in the matched element set OR
+ // Get the whole matched element set as a clean array
+ get: function( num ) {
+ return num === undefined ?
+
+ // Return a 'clean' array
+ Array.prototype.slice.call( this ) :
+
+ // Return just the object
+ this[ num ];
+ },
+
+ // Take an array of elements and push it onto the stack
+ // (returning the new matched element set)
+ pushStack: function( elems, name, selector ) {
+ // Build a new jQuery matched element set
+ var ret = jQuery( elems );
+
+ // Add the old object onto the stack (as a reference)
+ ret.prevObject = this;
+
+ ret.context = this.context;
+
+ if ( name === "find" )
+ ret.selector = this.selector + (this.selector ? " " : "") + selector;
+ else if ( name )
+ ret.selector = this.selector + "." + name + "(" + selector + ")";
+
+ // Return the newly-formed element set
+ return ret;
+ },
+
+ // Force the current matched set of elements to become
+ // the specified array of elements (destroying the stack in the process)
+ // You should use pushStack() in order to do this, but maintain the stack
+ setArray: function( elems ) {
+ // Resetting the length to 0, then using the native Array push
+ // is a super-fast way to populate an object with array-like properties
+ this.length = 0;
+ Array.prototype.push.apply( this, elems );
+
+ return this;
+ },
+
+ // Execute a callback for every element in the matched set.
+ // (You can seed the arguments with an array of args, but this is
+ // only used internally.)
+ each: function( callback, args ) {
+ return jQuery.each( this, callback, args );
+ },
+
+ // Determine the position of an element within
+ // the matched set of elements
+ index: function( elem ) {
+ // Locate the position of the desired element
+ return jQuery.inArray(
+ // If it receives a jQuery object, the first element is used
+ elem && elem.jquery ? elem[0] : elem
+ , this );
+ },
+
+ attr: function( name, value, type ) {
+ var options = name;
+
+ // Look for the case where we're accessing a style value
+ if ( typeof name === "string" )
+ if ( value === undefined )
+ return this[0] && jQuery[ type || "attr" ]( this[0], name );
+
+ else {
+ options = {};
+ options[ name ] = value;
+ }
+
+ // Check to see if we're setting style values
+ return this.each(function(i){
+ // Set all the styles
+ for ( name in options )
+ jQuery.attr(
+ type ?
+ this.style :
+ this,
+ name, jQuery.prop( this, options[ name ], type, i, name )
+ );
+ });
+ },
+
+ css: function( key, value ) {
+ // ignore negative width and height values
+ if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 )
+ value = undefined;
+ return this.attr( key, value, "curCSS" );
+ },
+
+ text: function( text ) {
+ if ( typeof text !== "object" && text != null )
+ return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
+
+ var ret = "";
+
+ jQuery.each( text || this, function(){
+ jQuery.each( this.childNodes, function(){
+ if ( this.nodeType != 8 )
+ ret += this.nodeType != 1 ?
+ this.nodeValue :
+ jQuery.fn.text( [ this ] );
+ });
+ });
+
+ return ret;
+ },
+
+ wrapAll: function( html ) {
+ if ( this[0] ) {
+ // The elements to wrap the target around
+ var wrap = jQuery( html, this[0].ownerDocument ).clone();
+
+ if ( this[0].parentNode )
+ wrap.insertBefore( this[0] );
+
+ wrap.map(function(){
+ var elem = this;
+
+ while ( elem.firstChild )
+ elem = elem.firstChild;
+
+ return elem;
+ }).append(this);
+ }
+
+ return this;
+ },
+
+ wrapInner: function( html ) {
+ return this.each(function(){
+ jQuery( this ).contents().wrapAll( html );
+ });
+ },
+
+ wrap: function( html ) {
+ return this.each(function(){
+ jQuery( this ).wrapAll( html );
+ });
+ },
+
+ append: function() {
+ return this.domManip(arguments, true, function(elem){
+ if (this.nodeType == 1)
+ this.appendChild( elem );
+ });
+ },
+
+ prepend: function() {
+ return this.domManip(arguments, true, function(elem){
+ if (this.nodeType == 1)
+ this.insertBefore( elem, this.firstChild );
+ });
+ },
+
+ before: function() {
+ return this.domManip(arguments, false, function(elem){
+ this.parentNode.insertBefore( elem, this );
+ });
+ },
+
+ after: function() {
+ return this.domManip(arguments, false, function(elem){
+ this.parentNode.insertBefore( elem, this.nextSibling );
+ });
+ },
+
+ end: function() {
+ return this.prevObject || jQuery( [] );
+ },
+
+ // For internal use only.
+ // Behaves like an Array's method, not like a jQuery method.
+ push: [].push,
+ sort: [].sort,
+ splice: [].splice,
+
+ find: function( selector ) {
+ if ( this.length === 1 ) {
+ var ret = this.pushStack( [], "find", selector );
+ ret.length = 0;
+ jQuery.find( selector, this[0], ret );
+ return ret;
+ } else {
+ return this.pushStack( jQuery.unique(jQuery.map(this, function(elem){
+ return jQuery.find( selector, elem );
+ })), "find", selector );
+ }
+ },
+
+ clone: function( events ) {
+ // Do the clone
+ var ret = this.map(function(){
+ if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) {
+ // IE copies events bound via attachEvent when
+ // using cloneNode. Calling detachEvent on the
+ // clone will also remove the events from the orignal
+ // In order to get around this, we use innerHTML.
+ // Unfortunately, this means some modifications to
+ // attributes in IE that are actually only stored
+ // as properties will not be copied (such as the
+ // the name attribute on an input).
+ var html = this.outerHTML;
+ if ( !html ) {
+ var div = this.ownerDocument.createElement("div");
+ div.appendChild( this.cloneNode(true) );
+ html = div.innerHTML;
+ }
+
+ return jQuery.clean([html.replace(/ jQuery\d+="(?:\d+|null)"/g, "").replace(/^\s*/, "")])[0];
+ } else
+ return this.cloneNode(true);
+ });
+
+ // Copy the events from the original to the clone
+ if ( events === true ) {
+ var orig = this.find("*").andSelf(), i = 0;
+
+ ret.find("*").andSelf().each(function(){
+ if ( this.nodeName !== orig[i].nodeName )
+ return;
+
+ var events = jQuery.data( orig[i], "events" );
+
+ for ( var type in events ) {
+ for ( var handler in events[ type ] ) {
+ jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data );
+ }
+ }
+
+ i++;
+ });
+ }
+
+ // Return the cloned set
+ return ret;
+ },
+
+ filter: function( selector ) {
+ return this.pushStack(
+ jQuery.isFunction( selector ) &&
+ jQuery.grep(this, function(elem, i){
+ return selector.call( elem, i );
+ }) ||
+
+ jQuery.multiFilter( selector, jQuery.grep(this, function(elem){
+ return elem.nodeType === 1;
+ }) ), "filter", selector );
+ },
+
+ closest: function( selector ) {
+ var pos = jQuery.expr.match.POS.test( selector ) ? jQuery(selector) : null,
+ closer = 0;
+
+ return this.map(function(){
+ var cur = this;
+ while ( cur && cur.ownerDocument ) {
+ if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selector) ) {
+ jQuery.data(cur, "closest", closer);
+ return cur;
+ }
+ cur = cur.parentNode;
+ closer++;
+ }
+ });
+ },
+
+ not: function( selector ) {
+ if ( typeof selector === "string" )
+ // test special case where just one selector is passed in
+ if ( isSimple.test( selector ) )
+ return this.pushStack( jQuery.multiFilter( selector, this, true ), "not", selector );
+ else
+ selector = jQuery.multiFilter( selector, this );
+
+ var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType;
+ return this.filter(function() {
+ return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector;
+ });
+ },
+
+ add: function( selector ) {
+ return this.pushStack( jQuery.unique( jQuery.merge(
+ this.get(),
+ typeof selector === "string" ?
+ jQuery( selector ) :
+ jQuery.makeArray( selector )
+ )));
+ },
+
+ is: function( selector ) {
+ return !!selector && jQuery.multiFilter( selector, this ).length > 0;
+ },
+
+ hasClass: function( selector ) {
+ return !!selector && this.is( "." + selector );
+ },
+
+ val: function( value ) {
+ if ( value === undefined ) {
+ var elem = this[0];
+
+ if ( elem ) {
+ if( jQuery.nodeName( elem, 'option' ) )
+ return (elem.attributes.value || {}).specified ? elem.value : elem.text;
+
+ // We need to handle select boxes special
+ if ( jQuery.nodeName( elem, "select" ) ) {
+ var index = elem.selectedIndex,
+ values = [],
+ options = elem.options,
+ one = elem.type == "select-one";
+
+ // Nothing was selected
+ if ( index < 0 )
+ return null;
+
+ // Loop through all the selected options
+ for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
+ var option = options[ i ];
+
+ if ( option.selected ) {
+ // Get the specifc value for the option
+ value = jQuery(option).val();
+
+ // We don't need an array for one selects
+ if ( one )
+ return value;
+
+ // Multi-Selects return an array
+ values.push( value );
+ }
+ }
+
+ return values;
+ }
+
+ // Everything else, we just grab the value
+ return (elem.value || "").replace(/\r/g, "");
+
+ }
+
+ return undefined;
+ }
+
+ if ( typeof value === "number" )
+ value += '';
+
+ return this.each(function(){
+ if ( this.nodeType != 1 )
+ return;
+
+ if ( jQuery.isArray(value) && /radio|checkbox/.test( this.type ) )
+ this.checked = (jQuery.inArray(this.value, value) >= 0 ||
+ jQuery.inArray(this.name, value) >= 0);
+
+ else if ( jQuery.nodeName( this, "select" ) ) {
+ var values = jQuery.makeArray(value);
+
+ jQuery( "option", this ).each(function(){
+ this.selected = (jQuery.inArray( this.value, values ) >= 0 ||
+ jQuery.inArray( this.text, values ) >= 0);
+ });
+
+ if ( !values.length )
+ this.selectedIndex = -1;
+
+ } else
+ this.value = value;
+ });
+ },
+
+ html: function( value ) {
+ return value === undefined ?
+ (this[0] ?
+ this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g, "") :
+ null) :
+ this.empty().append( value );
+ },
+
+ replaceWith: function( value ) {
+ return this.after( value ).remove();
+ },
+
+ eq: function( i ) {
+ return this.slice( i, +i + 1 );
+ },
+
+ slice: function() {
+ return this.pushStack( Array.prototype.slice.apply( this, arguments ),
+ "slice", Array.prototype.slice.call(arguments).join(",") );
+ },
+
+ map: function( callback ) {
+ return this.pushStack( jQuery.map(this, function(elem, i){
+ return callback.call( elem, i, elem );
+ }));
+ },
+
+ andSelf: function() {
+ return this.add( this.prevObject );
+ },
+
+ domManip: function( args, table, callback ) {
+ if ( this[0] ) {
+ var fragment = (this[0].ownerDocument || this[0]).createDocumentFragment(),
+ scripts = jQuery.clean( args, (this[0].ownerDocument || this[0]), fragment ),
+ first = fragment.firstChild;
+
+ if ( first )
+ for ( var i = 0, l = this.length; i < l; i++ )
+ callback.call( root(this[i], first), this.length > 1 || i > 0 ?
+ fragment.cloneNode(true) : fragment );
+
+ if ( scripts )
+ jQuery.each( scripts, evalScript );
+ }
+
+ return this;
+
+ function root( elem, cur ) {
+ return table && jQuery.nodeName(elem, "table") && jQuery.nodeName(cur, "tr") ?
+ (elem.getElementsByTagName("tbody")[0] ||
+ elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
+ elem;
+ }
+ }
+};
+
+// Give the init function the jQuery prototype for later instantiation
+jQuery.fn.init.prototype = jQuery.fn;
+
+function evalScript( i, elem ) {
+ if ( elem.src )
+ jQuery.ajax({
+ url: elem.src,
+ async: false,
+ dataType: "script"
+ });
+
+ else
+ jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" );
+
+ if ( elem.parentNode )
+ elem.parentNode.removeChild( elem );
+}
+
+function now(){
+ return +new Date;
+}
+
+jQuery.extend = jQuery.fn.extend = function() {
+ // copy reference to target object
+ var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options;
+
+ // Handle a deep copy situation
+ if ( typeof target === "boolean" ) {
+ deep = target;
+ target = arguments[1] || {};
+ // skip the boolean and the target
+ i = 2;
+ }
+
+ // Handle case when target is a string or something (possible in deep copy)
+ if ( typeof target !== "object" && !jQuery.isFunction(target) )
+ target = {};
+
+ // extend jQuery itself if only one argument is passed
+ if ( length == i ) {
+ target = this;
+ --i;
+ }
+
+ for ( ; i < length; i++ )
+ // Only deal with non-null/undefined values
+ if ( (options = arguments[ i ]) != null )
+ // Extend the base object
+ for ( var name in options ) {
+ var src = target[ name ], copy = options[ name ];
+
+ // Prevent never-ending loop
+ if ( target === copy )
+ continue;
+
+ // Recurse if we're merging object values
+ if ( deep && copy && typeof copy === "object" && !copy.nodeType )
+ target[ name ] = jQuery.extend( deep,
+ // Never move original objects, clone them
+ src || ( copy.length != null ? [ ] : { } )
+ , copy );
+
+ // Don't bring in undefined values
+ else if ( copy !== undefined )
+ target[ name ] = copy;
+
+ }
+
+ // Return the modified object
+ return target;
+};
+
+// exclude the following css properties to add px
+var exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i,
+ // cache defaultView
+ defaultView = document.defaultView || {},
+ toString = Object.prototype.toString;
+
+jQuery.extend({
+ noConflict: function( deep ) {
+ window.$ = _$;
+
+ if ( deep )
+ window.jQuery = _jQuery;
+
+ return jQuery;
+ },
+
+ // See test/unit/core.js for details concerning isFunction.
+ // Since version 1.3, DOM methods and functions like alert
+ // aren't supported. They return false on IE (#2968).
+ isFunction: function( obj ) {
+ return toString.call(obj) === "[object Function]";
+ },
+
+ isArray: function( obj ) {
+ return toString.call(obj) === "[object Array]";
+ },
+
+ // check if an element is in a (or is an) XML document
+ isXMLDoc: function( elem ) {
+ return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
+ !!elem.ownerDocument && jQuery.isXMLDoc( elem.ownerDocument );
+ },
+
+ // Evalulates a script in a global context
+ globalEval: function( data ) {
+ if ( data && /\S/.test(data) ) {
+ // Inspired by code by Andrea Giammarchi
+ // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.h…
+ var head = document.getElementsByTagName("head")[0] || document.documentElement,
+ script = document.createElement("script");
+
+ script.type = "text/javascript";
+ if ( jQuery.support.scriptEval )
+ script.appendChild( document.createTextNode( data ) );
+ else
+ script.text = data;
+
+ // Use insertBefore instead of appendChild to circumvent an IE6 bug.
+ // This arises when a base node is used (#2709).
+ head.insertBefore( script, head.firstChild );
+ head.removeChild( script );
+ }
+ },
+
+ nodeName: function( elem, name ) {
+ return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase();
+ },
+
+ // args is for internal usage only
+ each: function( object, callback, args ) {
+ var name, i = 0, length = object.length;
+
+ if ( args ) {
+ if ( length === undefined ) {
+ for ( name in object )
+ if ( callback.apply( object[ name ], args ) === false )
+ break;
+ } else
+ for ( ; i < length; )
+ if ( callback.apply( object[ i++ ], args ) === false )
+ break;
+
+ // A special, fast, case for the most common use of each
+ } else {
+ if ( length === undefined ) {
+ for ( name in object )
+ if ( callback.call( object[ name ], name, object[ name ] ) === false )
+ break;
+ } else
+ for ( var value = object[0];
+ i < length && callback.call( value, i, value ) !== false; value = object[++i] ){}
+ }
+
+ return object;
+ },
+
+ prop: function( elem, value, type, i, name ) {
+ // Handle executable functions
+ if ( jQuery.isFunction( value ) )
+ value = value.call( elem, i );
+
+ // Handle passing in a number to a CSS property
+ return typeof value === "number" && type == "curCSS" && !exclude.test( name ) ?
+ value + "px" :
+ value;
+ },
+
+ className: {
+ // internal only, use addClass("class")
+ add: function( elem, classNames ) {
+ jQuery.each((classNames || "").split(/\s+/), function(i, className){
+ if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) )
+ elem.className += (elem.className ? " " : "") + className;
+ });
+ },
+
+ // internal only, use removeClass("class")
+ remove: function( elem, classNames ) {
+ if (elem.nodeType == 1)
+ elem.className = classNames !== undefined ?
+ jQuery.grep(elem.className.split(/\s+/), function(className){
+ return !jQuery.className.has( classNames, className );
+ }).join(" ") :
+ "";
+ },
+
+ // internal only, use hasClass("class")
+ has: function( elem, className ) {
+ return elem && jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1;
+ }
+ },
+
+ // A method for quickly swapping in/out CSS properties to get correct calculations
+ swap: function( elem, options, callback ) {
+ var old = {};
+ // Remember the old values, and insert the new ones
+ for ( var name in options ) {
+ old[ name ] = elem.style[ name ];
+ elem.style[ name ] = options[ name ];
+ }
+
+ callback.call( elem );
+
+ // Revert the old values
+ for ( var name in options )
+ elem.style[ name ] = old[ name ];
+ },
+
+ css: function( elem, name, force, extra ) {
+ if ( name == "width" || name == "height" ) {
+ var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ];
+
+ function getWH() {
+ val = name == "width" ? elem.offsetWidth : elem.offsetHeight;
+
+ if ( extra === "border" )
+ return;
+
+ jQuery.each( which, function() {
+ if ( !extra )
+ val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0;
+ if ( extra === "margin" )
+ val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0;
+ else
+ val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0;
+ });
+ }
+
+ if ( elem.offsetWidth !== 0 )
+ getWH();
+ else
+ jQuery.swap( elem, props, getWH );
+
+ return Math.max(0, Math.round(val));
+ }
+
+ return jQuery.curCSS( elem, name, force );
+ },
+
+ curCSS: function( elem, name, force ) {
+ var ret, style = elem.style;
+
+ // We need to handle opacity special in IE
+ if ( name == "opacity" && !jQuery.support.opacity ) {
+ ret = jQuery.attr( style, "opacity" );
+
+ return ret == "" ?
+ "1" :
+ ret;
+ }
+
+ // Make sure we're using the right name for getting the float value
+ if ( name.match( /float/i ) )
+ name = styleFloat;
+
+ if ( !force && style && style[ name ] )
+ ret = style[ name ];
+
+ else if ( defaultView.getComputedStyle ) {
+
+ // Only "float" is needed here
+ if ( name.match( /float/i ) )
+ name = "float";
+
+ name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase();
+
+ var computedStyle = defaultView.getComputedStyle( elem, null );
+
+ if ( computedStyle )
+ ret = computedStyle.getPropertyValue( name );
+
+ // We should always get a number back from opacity
+ if ( name == "opacity" && ret == "" )
+ ret = "1";
+
+ } else if ( elem.currentStyle ) {
+ var camelCase = name.replace(/\-(\w)/g, function(all, letter){
+ return letter.toUpperCase();
+ });
+
+ ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ];
+
+ // From the awesome hack by Dean Edwards
+ // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+
+ // If we're not dealing with a regular pixel number
+ // but a number that has a weird ending, we need to convert it to pixels
+ if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) {
+ // Remember the original values
+ var left = style.left, rsLeft = elem.runtimeStyle.left;
+
+ // Put in the new values to get a computed value out
+ elem.runtimeStyle.left = elem.currentStyle.left;
+ style.left = ret || 0;
+ ret = style.pixelLeft + "px";
+
+ // Revert the changed values
+ style.left = left;
+ elem.runtimeStyle.left = rsLeft;
+ }
+ }
+
+ return ret;
+ },
+
+ clean: function( elems, context, fragment ) {
+ context = context || document;
+
+ // !context.createElement fails in IE with an error but returns typeof 'object'
+ if ( typeof context.createElement === "undefined" )
+ context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
+
+ // If a single string is passed in and it's a single tag
+ // just do a createElement and skip the rest
+ if ( !fragment && elems.length === 1 && typeof elems[0] === "string" ) {
+ var match = /^<(\w+)\s*\/?>$/.exec(elems[0]);
+ if ( match )
+ return [ context.createElement( match[1] ) ];
+ }
+
+ var ret = [], scripts = [], div = context.createElement("div");
+
+ jQuery.each(elems, function(i, elem){
+ if ( typeof elem === "number" )
+ elem += '';
+
+ if ( !elem )
+ return;
+
+ // Convert html string into DOM nodes
+ if ( typeof elem === "string" ) {
+ // Fix "XHTML"-style tags in all browsers
+ elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){
+ return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ?
+ all :
+ front + "></" + tag + ">";
+ });
+
+ // Trim whitespace, otherwise indexOf won't work as expected
+ var tags = elem.replace(/^\s+/, "").substring(0, 10).toLowerCase();
+
+ var wrap =
+ // option or optgroup
+ !tags.indexOf("<opt") &&
+ [ 1, "<select multiple='multiple'>", "</select>" ] ||
+
+ !tags.indexOf("<leg") &&
+ [ 1, "<fieldset>", "</fieldset>" ] ||
+
+ tags.match(/^<(thead|tbody|tfoot|colg|cap)/) &&
+ [ 1, "<table>", "</table>" ] ||
+
+ !tags.indexOf("<tr") &&
+ [ 2, "<table><tbody>", "</tbody></table>" ] ||
+
+ // <thead> matched above
+ (!tags.indexOf("<td") || !tags.indexOf("<th")) &&
+ [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ] ||
+
+ !tags.indexOf("<col") &&
+ [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ] ||
+
+ // IE can't serialize <link> and <script> tags normally
+ !jQuery.support.htmlSerialize &&
+ [ 1, "div<div>", "</div>" ] ||
+
+ [ 0, "", "" ];
+
+ // Go to html and back, then peel off extra wrappers
+ div.innerHTML = wrap[1] + elem + wrap[2];
+
+ // Move to the right depth
+ while ( wrap[0]-- )
+ div = div.lastChild;
+
+ // Remove IE's autoinserted <tbody> from table fragments
+ if ( !jQuery.support.tbody ) {
+
+ // String was a <table>, *may* have spurious <tbody>
+ var hasBody = /<tbody/i.test(elem),
+ tbody = !tags.indexOf("<table") && !hasBody ?
+ div.firstChild && div.firstChild.childNodes :
+
+ // String was a bare <thead> or <tfoot>
+ wrap[1] == "<table>" && !hasBody ?
+ div.childNodes :
+ [];
+
+ for ( var j = tbody.length - 1; j >= 0 ; --j )
+ if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length )
+ tbody[ j ].parentNode.removeChild( tbody[ j ] );
+
+ }
+
+ // IE completely kills leading whitespace when innerHTML is used
+ if ( !jQuery.support.leadingWhitespace && /^\s/.test( elem ) )
+ div.insertBefore( context.createTextNode( elem.match(/^\s*/)[0] ), div.firstChild );
+
+ elem = jQuery.makeArray( div.childNodes );
+ }
+
+ if ( elem.nodeType )
+ ret.push( elem );
+ else
+ ret = jQuery.merge( ret, elem );
+
+ });
+
+ if ( fragment ) {
+ for ( var i = 0; ret[i]; i++ ) {
+ if ( jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
+ scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );
+ } else {
+ if ( ret[i].nodeType === 1 )
+ ret.splice.apply( ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))) );
+ fragment.appendChild( ret[i] );
+ }
+ }
+
+ return scripts;
+ }
+
+ return ret;
+ },
+
+ attr: function( elem, name, value ) {
+ // don't set attributes on text and comment nodes
+ if (!elem || elem.nodeType == 3 || elem.nodeType == 8)
+ return undefined;
+
+ var notxml = !jQuery.isXMLDoc( elem ),
+ // Whether we are setting (or getting)
+ set = value !== undefined;
+
+ // Try to normalize/fix the name
+ name = notxml && jQuery.props[ name ] || name;
+
+ // Only do all the following if this is a node (faster for style)
+ // IE elem.getAttribute passes even for style
+ if ( elem.tagName ) {
+
+ // These attributes require special treatment
+ var special = /href|src|style/.test( name );
+
+ // Safari mis-reports the default selected property of a hidden option
+ // Accessing the parent's selectedIndex property fixes it
+ if ( name == "selected" && elem.parentNode )
+ elem.parentNode.selectedIndex;
+
+ // If applicable, access the attribute via the DOM 0 way
+ if ( name in elem && notxml && !special ) {
+ if ( set ){
+ // We can't allow the type property to be changed (since it causes problems in IE)
+ if ( name == "type" && jQuery.nodeName( elem, "input" ) && elem.parentNode )
+ throw "type property can't be changed";
+
+ elem[ name ] = value;
+ }
+
+ // browsers index elements by id/name on forms, give priority to attributes.
+ if( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) )
+ return elem.getAttributeNode( name ).nodeValue;
+
+ // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
+ // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabind…
+ if ( name == "tabIndex" ) {
+ var attributeNode = elem.getAttributeNode( "tabIndex" );
+ return attributeNode && attributeNode.specified
+ ? attributeNode.value
+ : elem.nodeName.match(/(button|input|object|select|textarea)/i)
+ ? 0
+ : elem.nodeName.match(/^(a|area)$/i) && elem.href
+ ? 0
+ : undefined;
+ }
+
+ return elem[ name ];
+ }
+
+ if ( !jQuery.support.style && notxml && name == "style" )
+ return jQuery.attr( elem.style, "cssText", value );
+
+ if ( set )
+ // convert the value to a string (all browsers do this but IE) see #1070
+ elem.setAttribute( name, "" + value );
+
+ var attr = !jQuery.support.hrefNormalized && notxml && special
+ // Some attributes require a special call on IE
+ ? elem.getAttribute( name, 2 )
+ : elem.getAttribute( name );
+
+ // Non-existent attributes return null, we normalize to undefined
+ return attr === null ? undefined : attr;
+ }
+
+ // elem is actually elem.style ... set the style
+
+ // IE uses filters for opacity
+ if ( !jQuery.support.opacity && name == "opacity" ) {
+ if ( set ) {
+ // IE has trouble with opacity if it does not have layout
+ // Force it by setting the zoom level
+ elem.zoom = 1;
+
+ // Set the alpha filter to set the opacity
+ elem.filter = (elem.filter || "").replace( /alpha\([^)]*\)/, "" ) +
+ (parseInt( value ) + '' == "NaN" ? "" : "alpha(opacity=" + value * 100 + ")");
+ }
+
+ return elem.filter && elem.filter.indexOf("opacity=") >= 0 ?
+ (parseFloat( elem.filter.match(/opacity=([^)]*)/)[1] ) / 100) + '':
+ "";
+ }
+
+ name = name.replace(/-([a-z])/ig, function(all, letter){
+ return letter.toUpperCase();
+ });
+
+ if ( set )
+ elem[ name ] = value;
+
+ return elem[ name ];
+ },
+
+ trim: function( text ) {
+ return (text || "").replace( /^\s+|\s+$/g, "" );
+ },
+
+ makeArray: function( array ) {
+ var ret = [];
+
+ if( array != null ){
+ var i = array.length;
+ // The window, strings (and functions) also have 'length'
+ if( i == null || typeof array === "string" || jQuery.isFunction(array) || array.setInterval )
+ ret[0] = array;
+ else
+ while( i )
+ ret[--i] = array[i];
+ }
+
+ return ret;
+ },
+
+ inArray: function( elem, array ) {
+ for ( var i = 0, length = array.length; i < length; i++ )
+ // Use === because on IE, window == document
+ if ( array[ i ] === elem )
+ return i;
+
+ return -1;
+ },
+
+ merge: function( first, second ) {
+ // We have to loop this way because IE & Opera overwrite the length
+ // expando of getElementsByTagName
+ var i = 0, elem, pos = first.length;
+ // Also, we need to make sure that the correct elements are being returned
+ // (IE returns comment nodes in a '*' query)
+ if ( !jQuery.support.getAll ) {
+ while ( (elem = second[ i++ ]) != null )
+ if ( elem.nodeType != 8 )
+ first[ pos++ ] = elem;
+
+ } else
+ while ( (elem = second[ i++ ]) != null )
+ first[ pos++ ] = elem;
+
+ return first;
+ },
+
+ unique: function( array ) {
+ var ret = [], done = {};
+
+ try {
+
+ for ( var i = 0, length = array.length; i < length; i++ ) {
+ var id = jQuery.data( array[ i ] );
+
+ if ( !done[ id ] ) {
+ done[ id ] = true;
+ ret.push( array[ i ] );
+ }
+ }
+
+ } catch( e ) {
+ ret = array;
+ }
+
+ return ret;
+ },
+
+ grep: function( elems, callback, inv ) {
+ var ret = [];
+
+ // Go through the array, only saving the items
+ // that pass the validator function
+ for ( var i = 0, length = elems.length; i < length; i++ )
+ if ( !inv != !callback( elems[ i ], i ) )
+ ret.push( elems[ i ] );
+
+ return ret;
+ },
+
+ map: function( elems, callback ) {
+ var ret = [];
+
+ // Go through the array, translating each of the items to their
+ // new value (or values).
+ for ( var i = 0, length = elems.length; i < length; i++ ) {
+ var value = callback( elems[ i ], i );
+
+ if ( value != null )
+ ret[ ret.length ] = value;
+ }
+
+ return ret.concat.apply( [], ret );
+ }
+});
+
+// Use of jQuery.browser is deprecated.
+// It's included for backwards compatibility and plugins,
+// although they should work to migrate away.
+
+var userAgent = navigator.userAgent.toLowerCase();
+
+// Figure out what browser is being used
+jQuery.browser = {
+ version: (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1],
+ safari: /webkit/.test( userAgent ),
+ opera: /opera/.test( userAgent ),
+ msie: /msie/.test( userAgent ) && !/opera/.test( userAgent ),
+ mozilla: /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent )
+};
+
+jQuery.each({
+ parent: function(elem){return elem.parentNode;},
+ parents: function(elem){return jQuery.dir(elem,"parentNode");},
+ next: function(elem){return jQuery.nth(elem,2,"nextSibling");},
+ prev: function(elem){return jQuery.nth(elem,2,"previousSibling");},
+ nextAll: function(elem){return jQuery.dir(elem,"nextSibling");},
+ prevAll: function(elem){return jQuery.dir(elem,"previousSibling");},
+ siblings: function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},
+ children: function(elem){return jQuery.sibling(elem.firstChild);},
+ contents: function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}
+}, function(name, fn){
+ jQuery.fn[ name ] = function( selector ) {
+ var ret = jQuery.map( this, fn );
+
+ if ( selector && typeof selector == "string" )
+ ret = jQuery.multiFilter( selector, ret );
+
+ return this.pushStack( jQuery.unique( ret ), name, selector );
+ };
+});
+
+jQuery.each({
+ appendTo: "append",
+ prependTo: "prepend",
+ insertBefore: "before",
+ insertAfter: "after",
+ replaceAll: "replaceWith"
+}, function(name, original){
+ jQuery.fn[ name ] = function( selector ) {
+ var ret = [], insert = jQuery( selector );
+
+ for ( var i = 0, l = insert.length; i < l; i++ ) {
+ var elems = (i > 0 ? this.clone(true) : this).get();
+ jQuery.fn[ original ].apply( jQuery(insert[i]), elems );
+ ret = ret.concat( elems );
+ }
+
+ return this.pushStack( ret, name, selector );
+ };
+});
+
+jQuery.each({
+ removeAttr: function( name ) {
+ jQuery.attr( this, name, "" );
+ if (this.nodeType == 1)
+ this.removeAttribute( name );
+ },
+
+ addClass: function( classNames ) {
+ jQuery.className.add( this, classNames );
+ },
+
+ removeClass: function( classNames ) {
+ jQuery.className.remove( this, classNames );
+ },
+
+ toggleClass: function( classNames, state ) {
+ if( typeof state !== "boolean" )
+ state = !jQuery.className.has( this, classNames );
+ jQuery.className[ state ? "add" : "remove" ]( this, classNames );
+ },
+
+ remove: function( selector ) {
+ if ( !selector || jQuery.filter( selector, [ this ] ).length ) {
+ // Prevent memory leaks
+ jQuery( "*", this ).add([this]).each(function(){
+ jQuery.event.remove(this);
+ jQuery.removeData(this);
+ });
+ if (this.parentNode)
+ this.parentNode.removeChild( this );
+ }
+ },
+
+ empty: function() {
+ // Remove element nodes and prevent memory leaks
+ jQuery(this).children().remove();
+
+ // Remove any remaining nodes
+ while ( this.firstChild )
+ this.removeChild( this.firstChild );
+ }
+}, function(name, fn){
+ jQuery.fn[ name ] = function(){
+ return this.each( fn, arguments );
+ };
+});
+
+// Helper function used by the dimensions and offset modules
+function num(elem, prop) {
+ return elem[0] && parseInt( jQuery.curCSS(elem[0], prop, true), 10 ) || 0;
+}
+var expando = "jQuery" + now(), uuid = 0, windowData = {};
+
+jQuery.extend({
+ cache: {},
+
+ data: function( elem, name, data ) {
+ elem = elem == window ?
+ windowData :
+ elem;
+
+ var id = elem[ expando ];
+
+ // Compute a unique ID for the element
+ if ( !id )
+ id = elem[ expando ] = ++uuid;
+
+ // Only generate the data cache if we're
+ // trying to access or manipulate it
+ if ( name && !jQuery.cache[ id ] )
+ jQuery.cache[ id ] = {};
+
+ // Prevent overriding the named cache with undefined values
+ if ( data !== undefined )
+ jQuery.cache[ id ][ name ] = data;
+
+ // Return the named cache data, or the ID for the element
+ return name ?
+ jQuery.cache[ id ][ name ] :
+ id;
+ },
+
+ removeData: function( elem, name ) {
+ elem = elem == window ?
+ windowData :
+ elem;
+
+ var id = elem[ expando ];
+
+ // If we want to remove a specific section of the element's data
+ if ( name ) {
+ if ( jQuery.cache[ id ] ) {
+ // Remove the section of cache data
+ delete jQuery.cache[ id ][ name ];
+
+ // If we've removed all the data, remove the element's cache
+ name = "";
+
+ for ( name in jQuery.cache[ id ] )
+ break;
+
+ if ( !name )
+ jQuery.removeData( elem );
+ }
+
+ // Otherwise, we want to remove all of the element's data
+ } else {
+ // Clean up the element expando
+ try {
+ delete elem[ expando ];
+ } catch(e){
+ // IE has trouble directly removing the expando
+ // but it's ok with using removeAttribute
+ if ( elem.removeAttribute )
+ elem.removeAttribute( expando );
+ }
+
+ // Completely remove the data cache
+ delete jQuery.cache[ id ];
+ }
+ },
+ queue: function( elem, type, data ) {
+ if ( elem ){
+
+ type = (type || "fx") + "queue";
+
+ var q = jQuery.data( elem, type );
+
+ if ( !q || jQuery.isArray(data) )
+ q = jQuery.data( elem, type, jQuery.makeArray(data) );
+ else if( data )
+ q.push( data );
+
+ }
+ return q;
+ },
+
+ dequeue: function( elem, type ){
+ var queue = jQuery.queue( elem, type ),
+ fn = queue.shift();
+
+ if( !type || type === "fx" )
+ fn = queue[0];
+
+ if( fn !== undefined )
+ fn.call(elem);
+ }
+});
+
+jQuery.fn.extend({
+ data: function( key, value ){
+ var parts = key.split(".");
+ parts[1] = parts[1] ? "." + parts[1] : "";
+
+ if ( value === undefined ) {
+ var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
+
+ if ( data === undefined && this.length )
+ data = jQuery.data( this[0], key );
+
+ return data === undefined && parts[1] ?
+ this.data( parts[0] ) :
+ data;
+ } else
+ return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){
+ jQuery.data( this, key, value );
+ });
+ },
+
+ removeData: function( key ){
+ return this.each(function(){
+ jQuery.removeData( this, key );
+ });
+ },
+ queue: function(type, data){
+ if ( typeof type !== "string" ) {
+ data = type;
+ type = "fx";
+ }
+
+ if ( data === undefined )
+ return jQuery.queue( this[0], type );
+
+ return this.each(function(){
+ var queue = jQuery.queue( this, type, data );
+
+ if( type == "fx" && queue.length == 1 )
+ queue[0].call(this);
+ });
+ },
+ dequeue: function(type){
+ return this.each(function(){
+ jQuery.dequeue( this, type );
+ });
+ }
+});/*!
+ * Sizzle CSS Selector Engine - v0.9.3
+ * Copyright 2009, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ * More information: http://sizzlejs.com/
+ */
+(function(){
+
+var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,
+ done = 0,
+ toString = Object.prototype.toString;
+
+var Sizzle = function(selector, context, results, seed) {
+ results = results || [];
+ context = context || document;
+
+ if ( context.nodeType !== 1 && context.nodeType !== 9 )
+ return [];
+
+ if ( !selector || typeof selector !== "string" ) {
+ return results;
+ }
+
+ var parts = [], m, set, checkSet, check, mode, extra, prune = true;
+
+ // Reset the position of the chunker regexp (start from head)
+ chunker.lastIndex = 0;
+
+ while ( (m = chunker.exec(selector)) !== null ) {
+ parts.push( m[1] );
+
+ if ( m[2] ) {
+ extra = RegExp.rightContext;
+ break;
+ }
+ }
+
+ if ( parts.length > 1 && origPOS.exec( selector ) ) {
+ if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
+ set = posProcess( parts[0] + parts[1], context );
+ } else {
+ set = Expr.relative[ parts[0] ] ?
+ [ context ] :
+ Sizzle( parts.shift(), context );
+
+ while ( parts.length ) {
+ selector = parts.shift();
+
+ if ( Expr.relative[ selector ] )
+ selector += parts.shift();
+
+ set = posProcess( selector, set );
+ }
+ }
+ } else {
+ var ret = seed ?
+ { expr: parts.pop(), set: makeArray(seed) } :
+ Sizzle.find( parts.pop(), parts.length === 1 && context.parentNode ? context.parentNode : context, isXML(context) );
+ set = Sizzle.filter( ret.expr, ret.set );
+
+ if ( parts.length > 0 ) {
+ checkSet = makeArray(set);
+ } else {
+ prune = false;
+ }
+
+ while ( parts.length ) {
+ var cur = parts.pop(), pop = cur;
+
+ if ( !Expr.relative[ cur ] ) {
+ cur = "";
+ } else {
+ pop = parts.pop();
+ }
+
+ if ( pop == null ) {
+ pop = context;
+ }
+
+ Expr.relative[ cur ]( checkSet, pop, isXML(context) );
+ }
+ }
+
+ if ( !checkSet ) {
+ checkSet = set;
+ }
+
+ if ( !checkSet ) {
+ throw "Syntax error, unrecognized expression: " + (cur || selector);
+ }
+
+ if ( toString.call(checkSet) === "[object Array]" ) {
+ if ( !prune ) {
+ results.push.apply( results, checkSet );
+ } else if ( context.nodeType === 1 ) {
+ for ( var i = 0; checkSet[i] != null; i++ ) {
+ if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
+ results.push( set[i] );
+ }
+ }
+ } else {
+ for ( var i = 0; checkSet[i] != null; i++ ) {
+ if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
+ results.push( set[i] );
+ }
+ }
+ }
+ } else {
+ makeArray( checkSet, results );
+ }
+
+ if ( extra ) {
+ Sizzle( extra, context, results, seed );
+
+ if ( sortOrder ) {
+ hasDuplicate = false;
+ results.sort(sortOrder);
+
+ if ( hasDuplicate ) {
+ for ( var i = 1; i < results.length; i++ ) {
+ if ( results[i] === results[i-1] ) {
+ results.splice(i--, 1);
+ }
+ }
+ }
+ }
+ }
+
+ return results;
+};
+
+Sizzle.matches = function(expr, set){
+ return Sizzle(expr, null, null, set);
+};
+
+Sizzle.find = function(expr, context, isXML){
+ var set, match;
+
+ if ( !expr ) {
+ return [];
+ }
+
+ for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
+ var type = Expr.order[i], match;
+
+ if ( (match = Expr.match[ type ].exec( expr )) ) {
+ var left = RegExp.leftContext;
+
+ if ( left.substr( left.length - 1 ) !== "\\" ) {
+ match[1] = (match[1] || "").replace(/\\/g, "");
+ set = Expr.find[ type ]( match, context, isXML );
+ if ( set != null ) {
+ expr = expr.replace( Expr.match[ type ], "" );
+ break;
+ }
+ }
+ }
+ }
+
+ if ( !set ) {
+ set = context.getElementsByTagName("*");
+ }
+
+ return {set: set, expr: expr};
+};
+
+Sizzle.filter = function(expr, set, inplace, not){
+ var old = expr, result = [], curLoop = set, match, anyFound,
+ isXMLFilter = set && set[0] && isXML(set[0]);
+
+ while ( expr && set.length ) {
+ for ( var type in Expr.filter ) {
+ if ( (match = Expr.match[ type ].exec( expr )) != null ) {
+ var filter = Expr.filter[ type ], found, item;
+ anyFound = false;
+
+ if ( curLoop == result ) {
+ result = [];
+ }
+
+ if ( Expr.preFilter[ type ] ) {
+ match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
+
+ if ( !match ) {
+ anyFound = found = true;
+ } else if ( match === true ) {
+ continue;
+ }
+ }
+
+ if ( match ) {
+ for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
+ if ( item ) {
+ found = filter( item, match, i, curLoop );
+ var pass = not ^ !!found;
+
+ if ( inplace && found != null ) {
+ if ( pass ) {
+ anyFound = true;
+ } else {
+ curLoop[i] = false;
+ }
+ } else if ( pass ) {
+ result.push( item );
+ anyFound = true;
+ }
+ }
+ }
+ }
+
+ if ( found !== undefined ) {
+ if ( !inplace ) {
+ curLoop = result;
+ }
+
+ expr = expr.replace( Expr.match[ type ], "" );
+
+ if ( !anyFound ) {
+ return [];
+ }
+
+ break;
+ }
+ }
+ }
+
+ // Improper expression
+ if ( expr == old ) {
+ if ( anyFound == null ) {
+ throw "Syntax error, unrecognized expression: " + expr;
+ } else {
+ break;
+ }
+ }
+
+ old = expr;
+ }
+
+ return curLoop;
+};
+
+var Expr = Sizzle.selectors = {
+ order: [ "ID", "NAME", "TAG" ],
+ match: {
+ ID: /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
+ CLASS: /\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
+ NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,
+ ATTR: /\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
+ TAG: /^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,
+ CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
+ POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
+ PSEUDO: /:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/
+ },
+ attrMap: {
+ "class": "className",
+ "for": "htmlFor"
+ },
+ attrHandle: {
+ href: function(elem){
+ return elem.getAttribute("href");
+ }
+ },
+ relative: {
+ "+": function(checkSet, part, isXML){
+ var isPartStr = typeof part === "string",
+ isTag = isPartStr && !/\W/.test(part),
+ isPartStrNotTag = isPartStr && !isTag;
+
+ if ( isTag && !isXML ) {
+ part = part.toUpperCase();
+ }
+
+ for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
+ if ( (elem = checkSet[i]) ) {
+ while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
+
+ checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?
+ elem || false :
+ elem === part;
+ }
+ }
+
+ if ( isPartStrNotTag ) {
+ Sizzle.filter( part, checkSet, true );
+ }
+ },
+ ">": function(checkSet, part, isXML){
+ var isPartStr = typeof part === "string";
+
+ if ( isPartStr && !/\W/.test(part) ) {
+ part = isXML ? part : part.toUpperCase();
+
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+ if ( elem ) {
+ var parent = elem.parentNode;
+ checkSet[i] = parent.nodeName === part ? parent : false;
+ }
+ }
+ } else {
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+ if ( elem ) {
+ checkSet[i] = isPartStr ?
+ elem.parentNode :
+ elem.parentNode === part;
+ }
+ }
+
+ if ( isPartStr ) {
+ Sizzle.filter( part, checkSet, true );
+ }
+ }
+ },
+ "": function(checkSet, part, isXML){
+ var doneName = done++, checkFn = dirCheck;
+
+ if ( !part.match(/\W/) ) {
+ var nodeCheck = part = isXML ? part : part.toUpperCase();
+ checkFn = dirNodeCheck;
+ }
+
+ checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
+ },
+ "~": function(checkSet, part, isXML){
+ var doneName = done++, checkFn = dirCheck;
+
+ if ( typeof part === "string" && !part.match(/\W/) ) {
+ var nodeCheck = part = isXML ? part : part.toUpperCase();
+ checkFn = dirNodeCheck;
+ }
+
+ checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
+ }
+ },
+ find: {
+ ID: function(match, context, isXML){
+ if ( typeof context.getElementById !== "undefined" && !isXML ) {
+ var m = context.getElementById(match[1]);
+ return m ? [m] : [];
+ }
+ },
+ NAME: function(match, context, isXML){
+ if ( typeof context.getElementsByName !== "undefined" ) {
+ var ret = [], results = context.getElementsByName(match[1]);
+
+ for ( var i = 0, l = results.length; i < l; i++ ) {
+ if ( results[i].getAttribute("name") === match[1] ) {
+ ret.push( results[i] );
+ }
+ }
+
+ return ret.length === 0 ? null : ret;
+ }
+ },
+ TAG: function(match, context){
+ return context.getElementsByTagName(match[1]);
+ }
+ },
+ preFilter: {
+ CLASS: function(match, curLoop, inplace, result, not, isXML){
+ match = " " + match[1].replace(/\\/g, "") + " ";
+
+ if ( isXML ) {
+ return match;
+ }
+
+ for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
+ if ( elem ) {
+ if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) {
+ if ( !inplace )
+ result.push( elem );
+ } else if ( inplace ) {
+ curLoop[i] = false;
+ }
+ }
+ }
+
+ return false;
+ },
+ ID: function(match){
+ return match[1].replace(/\\/g, "");
+ },
+ TAG: function(match, curLoop){
+ for ( var i = 0; curLoop[i] === false; i++ ){}
+ return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
+ },
+ CHILD: function(match){
+ if ( match[1] == "nth" ) {
+ // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
+ var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
+ match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" ||
+ !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
+
+ // calculate the numbers (first)n+(last) including if they are negative
+ match[2] = (test[1] + (test[2] || 1)) - 0;
+ match[3] = test[3] - 0;
+ }
+
+ // TODO: Move to normal caching system
+ match[0] = done++;
+
+ return match;
+ },
+ ATTR: function(match, curLoop, inplace, result, not, isXML){
+ var name = match[1].replace(/\\/g, "");
+
+ if ( !isXML && Expr.attrMap[name] ) {
+ match[1] = Expr.attrMap[name];
+ }
+
+ if ( match[2] === "~=" ) {
+ match[4] = " " + match[4] + " ";
+ }
+
+ return match;
+ },
+ PSEUDO: function(match, curLoop, inplace, result, not){
+ if ( match[1] === "not" ) {
+ // If we're dealing with a complex expression, or a simple one
+ if ( match[3].match(chunker).length > 1 || /^\w/.test(match[3]) ) {
+ match[3] = Sizzle(match[3], null, null, curLoop);
+ } else {
+ var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
+ if ( !inplace ) {
+ result.push.apply( result, ret );
+ }
+ return false;
+ }
+ } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
+ return true;
+ }
+
+ return match;
+ },
+ POS: function(match){
+ match.unshift( true );
+ return match;
+ }
+ },
+ filters: {
+ enabled: function(elem){
+ return elem.disabled === false && elem.type !== "hidden";
+ },
+ disabled: function(elem){
+ return elem.disabled === true;
+ },
+ checked: function(elem){
+ return elem.checked === true;
+ },
+ selected: function(elem){
+ // Accessing this property makes selected-by-default
+ // options in Safari work properly
+ elem.parentNode.selectedIndex;
+ return elem.selected === true;
+ },
+ parent: function(elem){
+ return !!elem.firstChild;
+ },
+ empty: function(elem){
+ return !elem.firstChild;
+ },
+ has: function(elem, i, match){
+ return !!Sizzle( match[3], elem ).length;
+ },
+ header: function(elem){
+ return /h\d/i.test( elem.nodeName );
+ },
+ text: function(elem){
+ return "text" === elem.type;
+ },
+ radio: function(elem){
+ return "radio" === elem.type;
+ },
+ checkbox: function(elem){
+ return "checkbox" === elem.type;
+ },
+ file: function(elem){
+ return "file" === elem.type;
+ },
+ password: function(elem){
+ return "password" === elem.type;
+ },
+ submit: function(elem){
+ return "submit" === elem.type;
+ },
+ image: function(elem){
+ return "image" === elem.type;
+ },
+ reset: function(elem){
+ return "reset" === elem.type;
+ },
+ button: function(elem){
+ return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON";
+ },
+ input: function(elem){
+ return /input|select|textarea|button/i.test(elem.nodeName);
+ }
+ },
+ setFilters: {
+ first: function(elem, i){
+ return i === 0;
+ },
+ last: function(elem, i, match, array){
+ return i === array.length - 1;
+ },
+ even: function(elem, i){
+ return i % 2 === 0;
+ },
+ odd: function(elem, i){
+ return i % 2 === 1;
+ },
+ lt: function(elem, i, match){
+ return i < match[3] - 0;
+ },
+ gt: function(elem, i, match){
+ return i > match[3] - 0;
+ },
+ nth: function(elem, i, match){
+ return match[3] - 0 == i;
+ },
+ eq: function(elem, i, match){
+ return match[3] - 0 == i;
+ }
+ },
+ filter: {
+ PSEUDO: function(elem, match, i, array){
+ var name = match[1], filter = Expr.filters[ name ];
+
+ if ( filter ) {
+ return filter( elem, i, match, array );
+ } else if ( name === "contains" ) {
+ return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0;
+ } else if ( name === "not" ) {
+ var not = match[3];
+
+ for ( var i = 0, l = not.length; i < l; i++ ) {
+ if ( not[i] === elem ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ },
+ CHILD: function(elem, match){
+ var type = match[1], node = elem;
+ switch (type) {
+ case 'only':
+ case 'first':
+ while (node = node.previousSibling) {
+ if ( node.nodeType === 1 ) return false;
+ }
+ if ( type == 'first') return true;
+ node = elem;
+ case 'last':
+ while (node = node.nextSibling) {
+ if ( node.nodeType === 1 ) return false;
+ }
+ return true;
+ case 'nth':
+ var first = match[2], last = match[3];
+
+ if ( first == 1 && last == 0 ) {
+ return true;
+ }
+
+ var doneName = match[0],
+ parent = elem.parentNode;
+
+ if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
+ var count = 0;
+ for ( node = parent.firstChild; node; node = node.nextSibling ) {
+ if ( node.nodeType === 1 ) {
+ node.nodeIndex = ++count;
+ }
+ }
+ parent.sizcache = doneName;
+ }
+
+ var diff = elem.nodeIndex - last;
+ if ( first == 0 ) {
+ return diff == 0;
+ } else {
+ return ( diff % first == 0 && diff / first >= 0 );
+ }
+ }
+ },
+ ID: function(elem, match){
+ return elem.nodeType === 1 && elem.getAttribute("id") === match;
+ },
+ TAG: function(elem, match){
+ return (match === "*" && elem.nodeType === 1) || elem.nodeName === match;
+ },
+ CLASS: function(elem, match){
+ return (" " + (elem.className || elem.getAttribute("class")) + " ")
+ .indexOf( match ) > -1;
+ },
+ ATTR: function(elem, match){
+ var name = match[1],
+ result = Expr.attrHandle[ name ] ?
+ Expr.attrHandle[ name ]( elem ) :
+ elem[ name ] != null ?
+ elem[ name ] :
+ elem.getAttribute( name ),
+ value = result + "",
+ type = match[2],
+ check = match[4];
+
+ return result == null ?
+ type === "!=" :
+ type === "=" ?
+ value === check :
+ type === "*=" ?
+ value.indexOf(check) >= 0 :
+ type === "~=" ?
+ (" " + value + " ").indexOf(check) >= 0 :
+ !check ?
+ value && result !== false :
+ type === "!=" ?
+ value != check :
+ type === "^=" ?
+ value.indexOf(check) === 0 :
+ type === "$=" ?
+ value.substr(value.length - check.length) === check :
+ type === "|=" ?
+ value === check || value.substr(0, check.length + 1) === check + "-" :
+ false;
+ },
+ POS: function(elem, match, i, array){
+ var name = match[2], filter = Expr.setFilters[ name ];
+
+ if ( filter ) {
+ return filter( elem, i, match, array );
+ }
+ }
+ }
+};
+
+var origPOS = Expr.match.POS;
+
+for ( var type in Expr.match ) {
+ Expr.match[ type ] = RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
+}
+
+var makeArray = function(array, results) {
+ array = Array.prototype.slice.call( array );
+
+ if ( results ) {
+ results.push.apply( results, array );
+ return results;
+ }
+
+ return array;
+};
+
+// Perform a simple check to determine if the browser is capable of
+// converting a NodeList to an array using builtin methods.
+try {
+ Array.prototype.slice.call( document.documentElement.childNodes );
+
+// Provide a fallback method if it does not work
+} catch(e){
+ makeArray = function(array, results) {
+ var ret = results || [];
+
+ if ( toString.call(array) === "[object Array]" ) {
+ Array.prototype.push.apply( ret, array );
+ } else {
+ if ( typeof array.length === "number" ) {
+ for ( var i = 0, l = array.length; i < l; i++ ) {
+ ret.push( array[i] );
+ }
+ } else {
+ for ( var i = 0; array[i]; i++ ) {
+ ret.push( array[i] );
+ }
+ }
+ }
+
+ return ret;
+ };
+}
+
+var sortOrder;
+
+if ( document.documentElement.compareDocumentPosition ) {
+ sortOrder = function( a, b ) {
+ var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
+ if ( ret === 0 ) {
+ hasDuplicate = true;
+ }
+ return ret;
+ };
+} else if ( "sourceIndex" in document.documentElement ) {
+ sortOrder = function( a, b ) {
+ var ret = a.sourceIndex - b.sourceIndex;
+ if ( ret === 0 ) {
+ hasDuplicate = true;
+ }
+ return ret;
+ };
+} else if ( document.createRange ) {
+ sortOrder = function( a, b ) {
+ var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
+ aRange.selectNode(a);
+ aRange.collapse(true);
+ bRange.selectNode(b);
+ bRange.collapse(true);
+ var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
+ if ( ret === 0 ) {
+ hasDuplicate = true;
+ }
+ return ret;
+ };
+}
+
+// Check to see if the browser returns elements by name when
+// querying by getElementById (and provide a workaround)
+(function(){
+ // We're going to inject a fake input element with a specified name
+ var form = document.createElement("form"),
+ id = "script" + (new Date).getTime();
+ form.innerHTML = "<input name='" + id + "'/>";
+
+ // Inject it into the root element, check its status, and remove it quickly
+ var root = document.documentElement;
+ root.insertBefore( form, root.firstChild );
+
+ // The workaround has to do additional checks after a getElementById
+ // Which slows things down for other browsers (hence the branching)
+ if ( !!document.getElementById( id ) ) {
+ Expr.find.ID = function(match, context, isXML){
+ if ( typeof context.getElementById !== "undefined" && !isXML ) {
+ var m = context.getElementById(match[1]);
+ return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
+ }
+ };
+
+ Expr.filter.ID = function(elem, match){
+ var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
+ return elem.nodeType === 1 && node && node.nodeValue === match;
+ };
+ }
+
+ root.removeChild( form );
+})();
+
+(function(){
+ // Check to see if the browser returns only elements
+ // when doing getElementsByTagName("*")
+
+ // Create a fake element
+ var div = document.createElement("div");
+ div.appendChild( document.createComment("") );
+
+ // Make sure no comments are found
+ if ( div.getElementsByTagName("*").length > 0 ) {
+ Expr.find.TAG = function(match, context){
+ var results = context.getElementsByTagName(match[1]);
+
+ // Filter out possible comments
+ if ( match[1] === "*" ) {
+ var tmp = [];
+
+ for ( var i = 0; results[i]; i++ ) {
+ if ( results[i].nodeType === 1 ) {
+ tmp.push( results[i] );
+ }
+ }
+
+ results = tmp;
+ }
+
+ return results;
+ };
+ }
+
+ // Check to see if an attribute returns normalized href attributes
+ div.innerHTML = "<a href='#'></a>";
+ if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
+ div.firstChild.getAttribute("href") !== "#" ) {
+ Expr.attrHandle.href = function(elem){
+ return elem.getAttribute("href", 2);
+ };
+ }
+})();
+
+if ( document.querySelectorAll ) (function(){
+ var oldSizzle = Sizzle, div = document.createElement("div");
+ div.innerHTML = "<p class='TEST'></p>";
+
+ // Safari can't handle uppercase or unicode characters when
+ // in quirks mode.
+ if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
+ return;
+ }
+
+ Sizzle = function(query, context, extra, seed){
+ context = context || document;
+
+ // Only use querySelectorAll on non-XML documents
+ // (ID selectors don't work in non-HTML documents)
+ if ( !seed && context.nodeType === 9 && !isXML(context) ) {
+ try {
+ return makeArray( context.querySelectorAll(query), extra );
+ } catch(e){}
+ }
+
+ return oldSizzle(query, context, extra, seed);
+ };
+
+ Sizzle.find = oldSizzle.find;
+ Sizzle.filter = oldSizzle.filter;
+ Sizzle.selectors = oldSizzle.selectors;
+ Sizzle.matches = oldSizzle.matches;
+})();
+
+if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){
+ var div = document.createElement("div");
+ div.innerHTML = "<div class='test e'></div><div class='test'></div>";
+
+ // Opera can't find a second classname (in 9.6)
+ if ( div.getElementsByClassName("e").length === 0 )
+ return;
+
+ // Safari caches class attributes, doesn't catch changes (in 3.2)
+ div.lastChild.className = "e";
+
+ if ( div.getElementsByClassName("e").length === 1 )
+ return;
+
+ Expr.order.splice(1, 0, "CLASS");
+ Expr.find.CLASS = function(match, context, isXML) {
+ if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
+ return context.getElementsByClassName(match[1]);
+ }
+ };
+})();
+
+function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+ var sibDir = dir == "previousSibling" && !isXML;
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+ if ( elem ) {
+ if ( sibDir && elem.nodeType === 1 ){
+ elem.sizcache = doneName;
+ elem.sizset = i;
+ }
+ elem = elem[dir];
+ var match = false;
+
+ while ( elem ) {
+ if ( elem.sizcache === doneName ) {
+ match = checkSet[elem.sizset];
+ break;
+ }
+
+ if ( elem.nodeType === 1 && !isXML ){
+ elem.sizcache = doneName;
+ elem.sizset = i;
+ }
+
+ if ( elem.nodeName === cur ) {
+ match = elem;
+ break;
+ }
+
+ elem = elem[dir];
+ }
+
+ checkSet[i] = match;
+ }
+ }
+}
+
+function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+ var sibDir = dir == "previousSibling" && !isXML;
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+ if ( elem ) {
+ if ( sibDir && elem.nodeType === 1 ) {
+ elem.sizcache = doneName;
+ elem.sizset = i;
+ }
+ elem = elem[dir];
+ var match = false;
+
+ while ( elem ) {
+ if ( elem.sizcache === doneName ) {
+ match = checkSet[elem.sizset];
+ break;
+ }
+
+ if ( elem.nodeType === 1 ) {
+ if ( !isXML ) {
+ elem.sizcache = doneName;
+ elem.sizset = i;
+ }
+ if ( typeof cur !== "string" ) {
+ if ( elem === cur ) {
+ match = true;
+ break;
+ }
+
+ } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
+ match = elem;
+ break;
+ }
+ }
+
+ elem = elem[dir];
+ }
+
+ checkSet[i] = match;
+ }
+ }
+}
+
+var contains = document.compareDocumentPosition ? function(a, b){
+ return a.compareDocumentPosition(b) & 16;
+} : function(a, b){
+ return a !== b && (a.contains ? a.contains(b) : true);
+};
+
+var isXML = function(elem){
+ return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
+ !!elem.ownerDocument && isXML( elem.ownerDocument );
+};
+
+var posProcess = function(selector, context){
+ var tmpSet = [], later = "", match,
+ root = context.nodeType ? [context] : context;
+
+ // Position selectors must be done after the filter
+ // And so must :not(positional) so we move all PSEUDOs to the end
+ while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
+ later += match[0];
+ selector = selector.replace( Expr.match.PSEUDO, "" );
+ }
+
+ selector = Expr.relative[selector] ? selector + "*" : selector;
+
+ for ( var i = 0, l = root.length; i < l; i++ ) {
+ Sizzle( selector, root[i], tmpSet );
+ }
+
+ return Sizzle.filter( later, tmpSet );
+};
+
+// EXPOSE
+jQuery.find = Sizzle;
+jQuery.filter = Sizzle.filter;
+jQuery.expr = Sizzle.selectors;
+jQuery.expr[":"] = jQuery.expr.filters;
+
+Sizzle.selectors.filters.hidden = function(elem){
+ return elem.offsetWidth === 0 || elem.offsetHeight === 0;
+};
+
+Sizzle.selectors.filters.visible = function(elem){
+ return elem.offsetWidth > 0 || elem.offsetHeight > 0;
+};
+
+Sizzle.selectors.filters.animated = function(elem){
+ return jQuery.grep(jQuery.timers, function(fn){
+ return elem === fn.elem;
+ }).length;
+};
+
+jQuery.multiFilter = function( expr, elems, not ) {
+ if ( not ) {
+ expr = ":not(" + expr + ")";
+ }
+
+ return Sizzle.matches(expr, elems);
+};
+
+jQuery.dir = function( elem, dir ){
+ var matched = [], cur = elem[dir];
+ while ( cur && cur != document ) {
+ if ( cur.nodeType == 1 )
+ matched.push( cur );
+ cur = cur[dir];
+ }
+ return matched;
+};
+
+jQuery.nth = function(cur, result, dir, elem){
+ result = result || 1;
+ var num = 0;
+
+ for ( ; cur; cur = cur[dir] )
+ if ( cur.nodeType == 1 && ++num == result )
+ break;
+
+ return cur;
+};
+
+jQuery.sibling = function(n, elem){
+ var r = [];
+
+ for ( ; n; n = n.nextSibling ) {
+ if ( n.nodeType == 1 && n != elem )
+ r.push( n );
+ }
+
+ return r;
+};
+
+return;
+
+window.Sizzle = Sizzle;
+
+})();
+/*
+ * A number of helper functions used for managing events.
+ * Many of the ideas behind this code originated from
+ * Dean Edwards' addEvent library.
+ */
+jQuery.event = {
+
+ // Bind an event to an element
+ // Original by Dean Edwards
+ add: function(elem, types, handler, data) {
+ if ( elem.nodeType == 3 || elem.nodeType == 8 )
+ return;
+
+ // For whatever reason, IE has trouble passing the window object
+ // around, causing it to be cloned in the process
+ if ( elem.setInterval && elem != window )
+ elem = window;
+
+ // Make sure that the function being executed has a unique ID
+ if ( !handler.guid )
+ handler.guid = this.guid++;
+
+ // if data is passed, bind to handler
+ if ( data !== undefined ) {
+ // Create temporary function pointer to original handler
+ var fn = handler;
+
+ // Create unique handler function, wrapped around original handler
+ handler = this.proxy( fn );
+
+ // Store data in unique handler
+ handler.data = data;
+ }
+
+ // Init the element's event structure
+ var events = jQuery.data(elem, "events") || jQuery.data(elem, "events", {}),
+ handle = jQuery.data(elem, "handle") || jQuery.data(elem, "handle", function(){
+ // Handle the second event of a trigger and when
+ // an event is called after a page has unloaded
+ return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
+ jQuery.event.handle.apply(arguments.callee.elem, arguments) :
+ undefined;
+ });
+ // Add elem as a property of the handle function
+ // This is to prevent a memory leak with non-native
+ // event in IE.
+ handle.elem = elem;
+
+ // Handle multiple events separated by a space
+ // jQuery(...).bind("mouseover mouseout", fn);
+ jQuery.each(types.split(/\s+/), function(index, type) {
+ // Namespaced event handlers
+ var namespaces = type.split(".");
+ type = namespaces.shift();
+ handler.type = namespaces.slice().sort().join(".");
+
+ // Get the current list of functions bound to this event
+ var handlers = events[type];
+
+ if ( jQuery.event.specialAll[type] )
+ jQuery.event.specialAll[type].setup.call(elem, data, namespaces);
+
+ // Init the event handler queue
+ if (!handlers) {
+ handlers = events[type] = {};
+
+ // Check for a special event handler
+ // Only use addEventListener/attachEvent if the special
+ // events handler returns false
+ if ( !jQuery.event.special[type] || jQuery.event.special[type].setup.call(elem, data, namespaces) === false ) {
+ // Bind the global event handler to the element
+ if (elem.addEventListener)
+ elem.addEventListener(type, handle, false);
+ else if (elem.attachEvent)
+ elem.attachEvent("on" + type, handle);
+ }
+ }
+
+ // Add the function to the element's handler list
+ handlers[handler.guid] = handler;
+
+ // Keep track of which events have been used, for global triggering
+ jQuery.event.global[type] = true;
+ });
+
+ // Nullify elem to prevent memory leaks in IE
+ elem = null;
+ },
+
+ guid: 1,
+ global: {},
+
+ // Detach an event or set of events from an element
+ remove: function(elem, types, handler) {
+ // don't do events on text and comment nodes
+ if ( elem.nodeType == 3 || elem.nodeType == 8 )
+ return;
+
+ var events = jQuery.data(elem, "events"), ret, index;
+
+ if ( events ) {
+ // Unbind all events for the element
+ if ( types === undefined || (typeof types === "string" && types.charAt(0) == ".") )
+ for ( var type in events )
+ this.remove( elem, type + (types || "") );
+ else {
+ // types is actually an event object here
+ if ( types.type ) {
+ handler = types.handler;
+ types = types.type;
+ }
+
+ // Handle multiple events seperated by a space
+ // jQuery(...).unbind("mouseover mouseout", fn);
+ jQuery.each(types.split(/\s+/), function(index, type){
+ // Namespaced event handlers
+ var namespaces = type.split(".");
+ type = namespaces.shift();
+ var namespace = RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)");
+
+ if ( events[type] ) {
+ // remove the given handler for the given type
+ if ( handler )
+ delete events[type][handler.guid];
+
+ // remove all handlers for the given type
+ else
+ for ( var handle in events[type] )
+ // Handle the removal of namespaced events
+ if ( namespace.test(events[type][handle].type) )
+ delete events[type][handle];
+
+ if ( jQuery.event.specialAll[type] )
+ jQuery.event.specialAll[type].teardown.call(elem, namespaces);
+
+ // remove generic event handler if no more handlers exist
+ for ( ret in events[type] ) break;
+ if ( !ret ) {
+ if ( !jQuery.event.special[type] || jQuery.event.special[type].teardown.call(elem, namespaces) === false ) {
+ if (elem.removeEventListener)
+ elem.removeEventListener(type, jQuery.data(elem, "handle"), false);
+ else if (elem.detachEvent)
+ elem.detachEvent("on" + type, jQuery.data(elem, "handle"));
+ }
+ ret = null;
+ delete events[type];
+ }
+ }
+ });
+ }
+
+ // Remove the expando if it's no longer used
+ for ( ret in events ) break;
+ if ( !ret ) {
+ var handle = jQuery.data( elem, "handle" );
+ if ( handle ) handle.elem = null;
+ jQuery.removeData( elem, "events" );
+ jQuery.removeData( elem, "handle" );
+ }
+ }
+ },
+
+ // bubbling is internal
+ trigger: function( event, data, elem, bubbling ) {
+ // Event object or event type
+ var type = event.type || event;
+
+ if( !bubbling ){
+ event = typeof event === "object" ?
+ // jQuery.Event object
+ event[expando] ? event :
+ // Object literal
+ jQuery.extend( jQuery.Event(type), event ) :
+ // Just the event type (string)
+ jQuery.Event(type);
+
+ if ( type.indexOf("!") >= 0 ) {
+ event.type = type = type.slice(0, -1);
+ event.exclusive = true;
+ }
+
+ // Handle a global trigger
+ if ( !elem ) {
+ // Don't bubble custom events when global (to avoid too much overhead)
+ event.stopPropagation();
+ // Only trigger if we've ever bound an event for it
+ if ( this.global[type] )
+ jQuery.each( jQuery.cache, function(){
+ if ( this.events && this.events[type] )
+ jQuery.event.trigger( event, data, this.handle.elem );
+ });
+ }
+
+ // Handle triggering a single element
+
+ // don't do events on text and comment nodes
+ if ( !elem || elem.nodeType == 3 || elem.nodeType == 8 )
+ return undefined;
+
+ // Clean up in case it is reused
+ event.result = undefined;
+ event.target = elem;
+
+ // Clone the incoming data, if any
+ data = jQuery.makeArray(data);
+ data.unshift( event );
+ }
+
+ event.currentTarget = elem;
+
+ // Trigger the event, it is assumed that "handle" is a function
+ var handle = jQuery.data(elem, "handle");
+ if ( handle )
+ handle.apply( elem, data );
+
+ // Handle triggering native .onfoo handlers (and on links since we don't call .click() for links)
+ if ( (!elem[type] || (jQuery.nodeName(elem, 'a') && type == "click")) && elem["on"+type] && elem["on"+type].apply( elem, data ) === false )
+ event.result = false;
+
+ // Trigger the native events (except for clicks on links)
+ if ( !bubbling && elem[type] && !event.isDefaultPrevented() && !(jQuery.nodeName(elem, 'a') && type == "click") ) {
+ this.triggered = true;
+ try {
+ elem[ type ]();
+ // prevent IE from throwing an error for some hidden elements
+ } catch (e) {}
+ }
+
+ this.triggered = false;
+
+ if ( !event.isPropagationStopped() ) {
+ var parent = elem.parentNode || elem.ownerDocument;
+ if ( parent )
+ jQuery.event.trigger(event, data, parent, true);
+ }
+ },
+
+ handle: function(event) {
+ // returned undefined or false
+ var all, handlers;
+
+ event = arguments[0] = jQuery.event.fix( event || window.event );
+ event.currentTarget = this;
+
+ // Namespaced event handlers
+ var namespaces = event.type.split(".");
+ event.type = namespaces.shift();
+
+ // Cache this now, all = true means, any handler
+ all = !namespaces.length && !event.exclusive;
+
+ var namespace = RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)");
+
+ handlers = ( jQuery.data(this, "events") || {} )[event.type];
+
+ for ( var j in handlers ) {
+ var handler = handlers[j];
+
+ // Filter the functions by class
+ if ( all || namespace.test(handler.type) ) {
+ // Pass in a reference to the handler function itself
+ // So that we can later remove it
+ event.handler = handler;
+ event.data = handler.data;
+
+ var ret = handler.apply(this, arguments);
+
+ if( ret !== undefined ){
+ event.result = ret;
+ if ( ret === false ) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ }
+
+ if( event.isImmediatePropagationStopped() )
+ break;
+
+ }
+ }
+ },
+
+ props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
+
+ fix: function(event) {
+ if ( event[expando] )
+ return event;
+
+ // store a copy of the original event object
+ // and "clone" to set read-only properties
+ var originalEvent = event;
+ event = jQuery.Event( originalEvent );
+
+ for ( var i = this.props.length, prop; i; ){
+ prop = this.props[ --i ];
+ event[ prop ] = originalEvent[ prop ];
+ }
+
+ // Fix target property, if necessary
+ if ( !event.target )
+ event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
+
+ // check if target is a textnode (safari)
+ if ( event.target.nodeType == 3 )
+ event.target = event.target.parentNode;
+
+ // Add relatedTarget, if necessary
+ if ( !event.relatedTarget && event.fromElement )
+ event.relatedTarget = event.fromElement == event.target ? event.toElement : event.fromElement;
+
+ // Calculate pageX/Y if missing and clientX/Y available
+ if ( event.pageX == null && event.clientX != null ) {
+ var doc = document.documentElement, body = document.body;
+ event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc.clientLeft || 0);
+ event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc.clientTop || 0);
+ }
+
+ // Add which for key events
+ if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) )
+ event.which = event.charCode || event.keyCode;
+
+ // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
+ if ( !event.metaKey && event.ctrlKey )
+ event.metaKey = event.ctrlKey;
+
+ // Add which for click: 1 == left; 2 == middle; 3 == right
+ // Note: button is not normalized, so don't use it
+ if ( !event.which && event.button )
+ event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
+
+ return event;
+ },
+
+ proxy: function( fn, proxy ){
+ proxy = proxy || function(){ return fn.apply(this, arguments); };
+ // Set the guid of unique handler to the same of original handler, so it can be removed
+ proxy.guid = fn.guid = fn.guid || proxy.guid || this.guid++;
+ // So proxy can be declared as an argument
+ return proxy;
+ },
+
+ special: {
+ ready: {
+ // Make sure the ready event is setup
+ setup: bindReady,
+ teardown: function() {}
+ }
+ },
+
+ specialAll: {
+ live: {
+ setup: function( selector, namespaces ){
+ jQuery.event.add( this, namespaces[0], liveHandler );
+ },
+ teardown: function( namespaces ){
+ if ( namespaces.length ) {
+ var remove = 0, name = RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)");
+
+ jQuery.each( (jQuery.data(this, "events").live || {}), function(){
+ if ( name.test(this.type) )
+ remove++;
+ });
+
+ if ( remove < 1 )
+ jQuery.event.remove( this, namespaces[0], liveHandler );
+ }
+ }
+ }
+ }
+};
+
+jQuery.Event = function( src ){
+ // Allow instantiation without the 'new' keyword
+ if( !this.preventDefault )
+ return new jQuery.Event(src);
+
+ // Event object
+ if( src && src.type ){
+ this.originalEvent = src;
+ this.type = src.type;
+ // Event type
+ }else
+ this.type = src;
+
+ // timeStamp is buggy for some events on Firefox(#3843)
+ // So we won't rely on the native value
+ this.timeStamp = now();
+
+ // Mark it as fixed
+ this[expando] = true;
+};
+
+function returnFalse(){
+ return false;
+}
+function returnTrue(){
+ return true;
+}
+
+// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
+// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-bindin…
+jQuery.Event.prototype = {
+ preventDefault: function() {
+ this.isDefaultPrevented = returnTrue;
+
+ var e = this.originalEvent;
+ if( !e )
+ return;
+ // if preventDefault exists run it on the original event
+ if (e.preventDefault)
+ e.preventDefault();
+ // otherwise set the returnValue property of the original event to false (IE)
+ e.returnValue = false;
+ },
+ stopPropagation: function() {
+ this.isPropagationStopped = returnTrue;
+
+ var e = this.originalEvent;
+ if( !e )
+ return;
+ // if stopPropagation exists run it on the original event
+ if (e.stopPropagation)
1
0
16 Feb '10
details: http://www.bx.psu.edu/hg/galaxy/rev/04fa6cbeb28c
changeset: 3395:04fa6cbeb28c
user: Greg Von Kuster <greg(a)bx.psu.edu>
date: Mon Feb 15 16:56:02 2010 -0500
description:
Sort the datasets by name when browsing a library from the Data Libraries view. Add a cleanup method to the functional test script for forms and requests - functional tests for libraries should now pass.
diffstat:
lib/galaxy/model/__init__.py | 20 ++++++++++++++++++--
lib/galaxy/web/controllers/library_admin.py | 11 +++++++----
lib/galaxy/web/controllers/library_common.py | 8 +++++---
test/functional/test_forms_and_requests.py | 17 +++++++++++++++++
test/functional/test_security_and_libraries.py | 5 +++--
5 files changed, 50 insertions(+), 11 deletions(-)
diffs (120 lines):
diff -r eeff3f4f9b81 -r 04fa6cbeb28c lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py Mon Feb 15 12:49:23 2010 -0500
+++ b/lib/galaxy/model/__init__.py Mon Feb 15 16:56:02 2010 -0500
@@ -5,7 +5,7 @@
the relationship cardinalities are obvious (e.g. prefer Dataset to Data)
"""
-import os.path, os, errno, sys, codecs
+import os.path, os, errno, sys, codecs, operator
import galaxy.datatypes
from galaxy.util.bunch import Bunch
from galaxy import util
@@ -848,8 +848,24 @@
return []
@property
def active_library_datasets( self ):
+ def sort_by_attr( seq, attr ):
+ """
+ Sort the sequence of objects by object's attribute
+ Arguments:
+ seq - the list or any sequence (including immutable one) of objects to sort.
+ attr - the name of attribute to sort by
+ """
+ # Use the "Schwartzian transform"
+ # Create the auxiliary list of tuples where every i-th tuple has form
+ # (seq[i].attr, i, seq[i]) and sort it. The second item of tuple is needed not
+ # only to provide stable sorting, but mainly to eliminate comparison of objects
+ # (which can be expensive or prohibited) in case of equal attribute values.
+ intermed = map( None, map( getattr, seq, ( attr, ) * len( seq ) ), xrange( len( seq ) ), seq )
+ intermed.sort()
+ return map( operator.getitem, intermed, ( -1, ) * len( intermed ) )
# This needs to be a list
- return [ ld for ld in self.datasets if not ld.library_dataset_dataset_association.deleted ]
+ active_library_datasets = [ ld for ld in self.datasets if not ld.library_dataset_dataset_association.deleted ]
+ return sort_by_attr( [ ld for ld in active_library_datasets ], 'name' )
@property
def activatable_library_datasets( self ):
# This needs to be a list
diff -r eeff3f4f9b81 -r 04fa6cbeb28c lib/galaxy/web/controllers/library_admin.py
--- a/lib/galaxy/web/controllers/library_admin.py Mon Feb 15 12:49:23 2010 -0500
+++ b/lib/galaxy/web/controllers/library_admin.py Mon Feb 15 16:56:02 2010 -0500
@@ -98,10 +98,13 @@
msg = util.restore_text( params.get( 'msg', '' ) )
messagetype = params.get( 'messagetype', 'done' )
if params.get( 'create_library_button', False ):
- library = trans.app.model.Library( name = util.restore_text( params.name ),
- description = util.restore_text( params.description ),
- synopsis = util.restore_text( params.synopsis ) )
- root_folder = trans.app.model.LibraryFolder( name = util.restore_text( params.name ), description = "" )
+ name = util.restore_text( params.get( 'name', 'No name' ) )
+ description = util.restore_text( params.get( 'description', '' ) )
+ synopsis = util.restore_text( params.get( 'synopsis', '' ) )
+ if synopsis in [ 'None', None ]:
+ synopsis = ''
+ library = trans.app.model.Library( name=name, description=description, synopsis=synopsis )
+ root_folder = trans.app.model.LibraryFolder( name=name, description='' )
library.root_folder = root_folder
trans.sa_session.add_all( ( library, root_folder ) )
trans.sa_session.flush()
diff -r eeff3f4f9b81 -r 04fa6cbeb28c lib/galaxy/web/controllers/library_common.py
--- a/lib/galaxy/web/controllers/library_common.py Mon Feb 15 12:49:23 2010 -0500
+++ b/lib/galaxy/web/controllers/library_common.py Mon Feb 15 16:56:02 2010 -0500
@@ -137,13 +137,15 @@
show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
if params.get( 'rename_library_button', False ):
old_name = library.name
- new_name = util.restore_text( params.name )
- new_description = util.restore_text( params.description )
- new_synopsis = util.restore_text( params.synopsis )
+ new_name = util.restore_text( params.get( 'name', 'No name' ) )
if not new_name:
msg = 'Enter a valid name'
messagetype='error'
else:
+ new_description = util.restore_text( params.get( 'description', '' ) )
+ new_synopsis = util.restore_text( params.get( 'synopsis', '' ) )
+ if new_synopsis in [ None, 'None' ]:
+ new_synopsis = ''
library.name = new_name
library.description = new_description
library.synopsis = new_synopsis
diff -r eeff3f4f9b81 -r 04fa6cbeb28c test/functional/test_forms_and_requests.py
--- a/test/functional/test_forms_and_requests.py Mon Feb 15 12:49:23 2010 -0500
+++ b/test/functional/test_forms_and_requests.py Mon Feb 15 16:56:02 2010 -0500
@@ -312,3 +312,20 @@
# check if the request's state is now set to 'submitted'
assert request_two.state is not request_two.states.REJECTED, "The state of the request '%s' should be set to '%s'" \
% ( request_two.name, request_two.states.REJECTED )
+ def test_050_reset_data_for_later_test_runs( self ):
+ """Reseting data to enable later test runs to pass"""
+ # TODO: RC: add whatever is missing from this method that should be marked
+ # deleted or purged so that later test runs will correctly test features if the
+ # database has not be purged.
+ #
+ # Logged in as admin_user
+ ##################
+ # Eliminate all non-private roles
+ ##################
+ for role in [ role_one ]:
+ self.mark_role_deleted( self.security.encode_id( role.id ), role.name )
+ self.purge_role( self.security.encode_id( role.id ), role.name )
+ # Manually delete the role from the database
+ sa_session.refresh( role )
+ sa_session.delete( role )
+ sa_session.flush()
diff -r eeff3f4f9b81 -r 04fa6cbeb28c test/functional/test_security_and_libraries.py
--- a/test/functional/test_security_and_libraries.py Mon Feb 15 12:49:23 2010 -0500
+++ b/test/functional/test_security_and_libraries.py Mon Feb 15 16:56:02 2010 -0500
@@ -113,8 +113,9 @@
.first()
# Make sure DatasetPermissions is correct - default is 'manage permissions'
if len( latest_dataset.actions ) > 1:
- raise AssertionError( '%d DatasetPermissions were created for dataset id %d when it was created ( should have been 1 )' \
- % ( len( latest_dataset.actions ), latest_dataset.id ) )
+ actions = [ a.action for a in latest_dataset.actions ]
+ raise AssertionError( '%d DatasetPermissions (%s) were created for dataset id %d when it was created ( should have been 1 )' \
+ % ( len( latest_dataset.actions ), str( actions ), latest_dataset.id ) )
dp = sa_session.query( galaxy.model.DatasetPermissions ) \
.filter( galaxy.model.DatasetPermissions.table.c.dataset_id==latest_dataset.id ) \
.first()
1
0
15 Feb '10
details: http://www.bx.psu.edu/hg/galaxy/rev/eeff3f4f9b81
changeset: 3394:eeff3f4f9b81
user: Greg Von Kuster <greg(a)bx.psu.edu>
date: Mon Feb 15 12:49:23 2010 -0500
description:
Do not display the work None if the libray synopsis is blank - I know there is a better way to do this, but I cannot think of it right now...
diffstat:
templates/library/common/browse_library.mako | 10 ++++++----
1 files changed, 6 insertions(+), 4 deletions(-)
diffs (20 lines):
diff -r ea63aa4a9ff0 -r eeff3f4f9b81 templates/library/common/browse_library.mako
--- a/templates/library/common/browse_library.mako Mon Feb 15 12:30:49 2010 -0500
+++ b/templates/library/common/browse_library.mako Mon Feb 15 12:49:23 2010 -0500
@@ -413,10 +413,12 @@
${render_msg( msg, messagetype )}
%endif
- <div class="libraryItemBody">
- ${library.synopsis}
- </div>
- <br/>
+ %if library.synopsis not in [ 'None', None ]:
+ <div class="libraryItemBody">
+ ${library.synopsis}
+ </div>
+ <br/>
+ %endif
<form name="act_on_multiple_datasets" action="${h.url_for( controller='library_common', action='act_on_multiple_datasets', cntrller=cntrller, library_id=trans.security.encode_id( library.id ) )}" onSubmit="javascript:return checkForm();" method="post">
<table cellspacing="0" cellpadding="0" border="0" width="100%" class="grid" id="library-grid">
1
0
15 Feb '10
details: http://www.bx.psu.edu/hg/galaxy/rev/ea63aa4a9ff0
changeset: 3393:ea63aa4a9ff0
user: Greg Von Kuster <greg(a)bx.psu.edu>
date: Mon Feb 15 12:30:49 2010 -0500
description:
Add a synopsis column to the library table, fix displaying the mast head when accessing a data library from an external url, and eliminate the display of all non-private roles on permissions forms.
diffstat:
lib/galaxy/model/__init__.py | 3 +-
lib/galaxy/model/mapping.py | 3 +-
lib/galaxy/model/migrate/versions/0039_add_synopsis_column_to_library_table.py | 24 +
lib/galaxy/security/__init__.py | 74 +-
lib/galaxy/web/controllers/library_admin.py | 5 +-
lib/galaxy/web/controllers/library_common.py | 9 +-
templates/admin/library/new_library.mako | 11 +-
templates/library/common/browse_library.mako | 936 +++++----
templates/library/common/library_info.mako | 18 +
test/base/twilltestcase.py | 18 +-
test/functional/test_forms_and_requests.py | 15 +-
test/functional/test_security_and_libraries.py | 82 +-
tool_conf.xml.main | 4 +-
13 files changed, 691 insertions(+), 511 deletions(-)
diffs (1586 lines):
diff -r 886f816b8f70 -r ea63aa4a9ff0 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py Mon Feb 15 11:16:49 2010 -0500
+++ b/lib/galaxy/model/__init__.py Mon Feb 15 12:30:49 2010 -0500
@@ -749,9 +749,10 @@
class Library( object ):
permitted_actions = get_permitted_actions( filter='LIBRARY' )
- def __init__( self, name = None, description = None, root_folder = None ):
+ def __init__( self, name=None, description=None, synopsis=None, root_folder=None ):
self.name = name or "Unnamed library"
self.description = description
+ self.synopsis = synopsis
self.root_folder = root_folder
def get_info_association( self, restrict=False, inherited=False ):
if self.info_association:
diff -r 886f816b8f70 -r ea63aa4a9ff0 lib/galaxy/model/mapping.py
--- a/lib/galaxy/model/mapping.py Mon Feb 15 11:16:49 2010 -0500
+++ b/lib/galaxy/model/mapping.py Mon Feb 15 12:30:49 2010 -0500
@@ -274,7 +274,8 @@
Column( "name", String( 255 ), index=True ),
Column( "deleted", Boolean, index=True, default=False ),
Column( "purged", Boolean, index=True, default=False ),
- Column( "description", TEXT ) )
+ Column( "description", TEXT ),
+ Column( "synopsis", TEXT ) )
LibraryFolder.table = Table( "library_folder", metadata,
Column( "id", Integer, primary_key=True ),
diff -r 886f816b8f70 -r ea63aa4a9ff0 lib/galaxy/model/migrate/versions/0039_add_synopsis_column_to_library_table.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/galaxy/model/migrate/versions/0039_add_synopsis_column_to_library_table.py Mon Feb 15 12:30:49 2010 -0500
@@ -0,0 +1,24 @@
+"""
+Migration script to add a synopsis column to the library table.
+"""
+
+from sqlalchemy import *
+from migrate import *
+from migrate.changeset import *
+
+import logging
+log = logging.getLogger( __name__ )
+
+metadata = MetaData( migrate_engine )
+
+def upgrade():
+ print __doc__
+ metadata.reflect()
+
+ Library_table = Table( "library", metadata, autoload=True )
+ c = Column( "synopsis", TEXT )
+ c.create( Library_table )
+ assert c is Library_table.c.synopsis
+
+def downgrade():
+ pass
diff -r 886f816b8f70 -r ea63aa4a9ff0 lib/galaxy/security/__init__.py
--- a/lib/galaxy/security/__init__.py Mon Feb 15 11:16:49 2010 -0500
+++ b/lib/galaxy/security/__init__.py Mon Feb 15 12:30:49 2010 -0500
@@ -112,17 +112,12 @@
def get_legitimate_roles( self, trans, item ):
"""
Return a sorted list of legitimate roles that can be associated with a permission on
- item where item is a Library or a Dataset. If item is public, all roles are legitimate.
- If item is restricted, legitimate roles are derived from the users and groups associated
- with each role that is associated with the access permission ( i.e., DATASET_MANAGE_PERMISSIONS
- or LIBRARY_MANAGE ) on item.
+ item where item is a Library or a Dataset. If item is public, all non-private roles,
+ except for the current user's private role, are legitimate. If item is restricted,
+ legitimate roles are derived from the users and groups associated with each role that
+ is associated with the access permission ( i.e., DATASET_MANAGE_PERMISSIONS or
+ LIBRARY_MANAGE ) on item.
"""
- if ( isinstance( item, self.model.Library ) and self.library_is_public( item ) ) or \
- ( isinstance( item, self.model.Dataset ) and self.dataset_is_public( item ) ):
- # If item is public, private roles will always be allowed
- return trans.sa_session.query( trans.app.model.Role ) \
- .filter( trans.app.model.Role.table.c.deleted==False ) \
- .order_by( trans.app.model.Role.table.c.name )
def sort_by_attr( seq, attr ):
"""
Sort the sequence of objects by object's attribute
@@ -137,26 +132,55 @@
# (which can be expensive or prohibited) in case of equal attribute values.
intermed = map( None, map( getattr, seq, ( attr, ) * len( seq ) ), xrange( len( seq ) ), seq )
intermed.sort()
- return map( operator.getitem, intermed, ( -1, ) * len( intermed ) )
+ return map( operator.getitem, intermed, ( -1, ) * len( intermed ) )
roles = set()
+ if ( isinstance( item, self.model.Library ) and self.library_is_public( item ) ) or \
+ ( isinstance( item, self.model.Dataset ) and self.dataset_is_public( item ) ):
+ if not trans.user:
+ return trans.sa_session.query( trans.app.model.Role ) \
+ .filter( and_( self.model.Role.table.c.deleted==False,
+ self.model.Role.table.c.type != self.model.Role.types.PRIVATE ) ) \
+ .order_by( self.model.Role.table.c.name )
+ # Add the current user's private role
+ roles.add( self.get_private_user_role( trans.user ) )
+ for role in trans.sa_session.query( trans.app.model.Role ) \
+ .filter( and_( self.model.Role.table.c.deleted==False,
+ self.model.Role.table.c.type != self.model.Role.types.PRIVATE ) ) \
+ .order_by( self.model.Role.table.c.name ):
+ roles.add( role )
+ return sort_by_attr( [ role for role in roles ], 'name' )
# If item has roles associated with the access permission, we need to start with them.
access_roles = item.get_access_roles( trans )
for role in access_roles:
- roles.add( role )
- # Each role potentially has users. We need to find all roles that each of those users have.
- for ura in role.users:
- user = ura.user
- for ura2 in user.roles:
- roles.add( ura2.role )
- # Each role also potentially has groups which, in turn, have members ( users ). We need to
- # find all roles that each group's members have.
- for gra in role.groups:
- group = gra.group
- for uga in group.users:
- user = uga.user
- for ura in user.roles:
- roles.add( ura.role )
+ if self.ok_to_display( trans, role ):
+ roles.add( role )
+ # Each role potentially has users. We need to find all roles that each of those users have.
+ for ura in role.users:
+ user = ura.user
+ for ura2 in user.roles:
+ if self.ok_to_display( trans, ura2.role ):
+ roles.add( ura2.role )
+ # Each role also potentially has groups which, in turn, have members ( users ). We need to
+ # find all roles that each group's members have.
+ for gra in role.groups:
+ group = gra.group
+ for uga in group.users:
+ user = uga.user
+ for ura in user.roles:
+ if self.ok_to_display( trans, ura.role ):
+ roles.add( ura.role )
return sort_by_attr( [ role for role in roles ], 'name' )
+ def ok_to_display( self, trans, role ):
+ """
+ Method for checking if a role is not private, unless it is the current user's
+ private role. Private roles, except for the current user's private role, are
+ never displayed, no matter what.
+ """
+ if trans.user and ( role.type != self.model.Role.types.PRIVATE or role == self.get_private_user_role( trans.user ) ):
+ return True
+ if not trans.user and role.type != self.model.Role.types.PRIVATE:
+ return True
+ return False
def allow_action( self, roles, action, item ):
"""
Method for checking a permission for the current user ( based on roles ) to perform a
diff -r 886f816b8f70 -r ea63aa4a9ff0 lib/galaxy/web/controllers/library_admin.py
--- a/lib/galaxy/web/controllers/library_admin.py Mon Feb 15 11:16:49 2010 -0500
+++ b/lib/galaxy/web/controllers/library_admin.py Mon Feb 15 12:30:49 2010 -0500
@@ -97,9 +97,10 @@
params = util.Params( kwd )
msg = util.restore_text( params.get( 'msg', '' ) )
messagetype = params.get( 'messagetype', 'done' )
- if params.get( 'new', False ):
+ if params.get( 'create_library_button', False ):
library = trans.app.model.Library( name = util.restore_text( params.name ),
- description = util.restore_text( params.description ) )
+ description = util.restore_text( params.description ),
+ synopsis = util.restore_text( params.synopsis ) )
root_folder = trans.app.model.LibraryFolder( name = util.restore_text( params.name ), description = "" )
library.root_folder = root_folder
trans.sa_session.add_all( ( library, root_folder ) )
diff -r 886f816b8f70 -r ea63aa4a9ff0 lib/galaxy/web/controllers/library_common.py
--- a/lib/galaxy/web/controllers/library_common.py Mon Feb 15 11:16:49 2010 -0500
+++ b/lib/galaxy/web/controllers/library_common.py Mon Feb 15 12:30:49 2010 -0500
@@ -92,6 +92,10 @@
msg = "Invalid library id ( %s )." % str( library_id )
messagetype = 'error'
else:
+ # If use_panels is True, the library is being accessed via an external link
+ # which did not originate from within the Galaxy instance, and the library will
+ # be displayed correctly with the mast head.
+ use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
created_ldda_ids = params.get( 'created_ldda_ids', '' )
hidden_folder_ids = util.listify( params.get( 'hidden_folder_ids', '' ) )
@@ -103,6 +107,7 @@
msg += "message \"This job is running\" is cleared from the \"Information\" column below for each selected dataset."
messagetype = "info"
return trans.fill_template( '/library/common/browse_library.mako',
+ use_panels=use_panels,
cntrller=cntrller,
library=library,
created_ldda_ids=created_ldda_ids,
@@ -134,12 +139,14 @@
old_name = library.name
new_name = util.restore_text( params.name )
new_description = util.restore_text( params.description )
+ new_synopsis = util.restore_text( params.synopsis )
if not new_name:
msg = 'Enter a valid name'
messagetype='error'
else:
library.name = new_name
library.description = new_description
+ library.synopsis = new_synopsis
# Rename the root_folder
library.root_folder.name = new_name
library.root_folder.description = new_description
@@ -249,7 +256,7 @@
msg=msg,
messagetype='done' )
# If not inheritable info_association, redirect to the library.
- msg = "The new folder named '%s' has been added to the library" % new_folder.name
+ msg = "The new folder named '%s' has been added to the data library." % new_folder.name
return trans.response.send_redirect( web.url_for( controller='library_common',
action='browse_library',
cntrller=cntrller,
diff -r 886f816b8f70 -r ea63aa4a9ff0 templates/admin/library/new_library.mako
--- a/templates/admin/library/new_library.mako Mon Feb 15 11:16:49 2010 -0500
+++ b/templates/admin/library/new_library.mako Mon Feb 15 12:30:49 2010 -0500
@@ -21,13 +21,20 @@
<div style="float: left; width: 250px; margin-right: 10px;">
<input type="text" name="description" value="" size="40"/>
</div>
+ <div class="toolParamHelp" style="clear: both;">
+ Displayed when browsing all libraries
+ </div>
<div style="clear: both"></div>
</div>
<div class="form-row">
+ <label>Synopsis:</label>
<div style="float: left; width: 250px; margin-right: 10px;">
- <input type="hidden" name="new" value="submitted" size="40"/>
+ <input type="text" name="synopsis" value="" size="40"/>
</div>
- <div style="clear: both"></div>
+ <div class="toolParamHelp" style="clear: both;">
+ Displayed when browsing this library
+ </div>
+ <div style="clear: both"></div>
</div>
<div class="form-row">
<input type="submit" name="create_library_button" value="Create"/>
diff -r 886f816b8f70 -r ea63aa4a9ff0 templates/library/common/browse_library.mako
--- a/templates/library/common/browse_library.mako Mon Feb 15 11:16:49 2010 -0500
+++ b/templates/library/common/browse_library.mako Mon Feb 15 12:30:49 2010 -0500
@@ -1,464 +1,512 @@
-<%inherit file="/base.mako"/>
<%namespace file="/message.mako" import="render_msg" />
<%namespace file="/library/common/library_item_info.mako" import="render_library_item_info" />
<%namespace file="/library/common/common.mako" import="render_actions_on_multiple_items" />
-<%
- from galaxy import util
- from galaxy.web.controllers.library_common import active_folders, active_folders_and_lddas, activatable_folders_and_lddas, branch_deleted
- from time import strftime
+
+<%!
+ def inherit(context):
+ if context.get('use_panels'):
+ return '/base_panels.mako'
+ else:
+ return '/base.mako'
%>
+<%inherit file="${inherit(context)}"/>
+
+<%def name="init()">
+<%
+ self.has_left_panel=False
+ self.has_right_panel=False
+ self.message_box_visible=False
+ self.active_view="user"
+ self.overlay_visible=False
+%>
+</%def>
+
+##
+## Override methods from base.mako and base_panels.mako
+##
+<%def name="center_panel()">
+ <div style="overflow: auto; height: 100%;">
+ <div class="page-container" style="padding: 10px;">
+ ${render_content()}
+ </div>
+ </div>
+ ##${render_content()}
+</%def>
+
+## Render the grid's basic elements. Each of these elements can be subclassed.
+<%def name="body()">
+ ${render_content()}
+</%def>
<%def name="title()">Browse data library</%def>
<%def name="stylesheets()">
- <link href="${h.url_for('/static/style/base.css')}" rel="stylesheet" type="text/css" />
- <link href="${h.url_for('/static/style/library.css')}" rel="stylesheet" type="text/css" />
+ ${parent.stylesheets()}
+ ${h.css( "library" )}
</%def>
-<%
- if cntrller in [ 'library', 'requests' ]:
- can_add = trans.app.security_agent.can_add_library_item( current_user_roles, library )
- can_modify = trans.app.security_agent.can_modify_library_item( current_user_roles, library )
- can_manage = trans.app.security_agent.can_manage_library_item( current_user_roles, library )
- info_association, inherited = library.get_info_association()
-
- tracked_datasets = {}
-
- class RowCounter( object ):
- def __init__( self ):
- self.count = 0
- def increment( self ):
- self.count += 1
- def __str__( self ):
- return str( self.count )
-%>
-
-<script type="text/javascript">
- $( document ).ready( function () {
- $("#library-grid").each( function() {
- // Recursively fill in children and descendents of each row
- var process_row = function( q, parents ) {
- // Find my index
- var index = $(q).parent().children().index( $(q) );
- // Find my immediate children
- var children = $(q).siblings().filter( "[parent='" + index + "']" );
- // Recursively handle them
- var descendents = children;
- children.each( function() {
- child_descendents = process_row( $(this), parents.add( q ) );
- descendents = descendents.add( child_descendents );
- });
- // Set up expand / hide link
- // HACK: assume descendents are invisible. The caller actually
- // ensures this for the root node. However, if we start
- // remembering folder states, we'll need something
- // more sophisticated here.
- var visible = false;
- $(q).find( "span.expandLink").click( function() {
- if ( visible ) {
- descendents.hide();
- descendents.removeClass( "expanded" );
- q.removeClass( "expanded" );
- visible = false;
- } else {
- children.show();
- q.addClass( "expanded" );
- visible = true;
- }
- });
- // Check/uncheck boxes in subfolders.
- q.children( "td" ).children( "input[type=checkbox]" ).click( function() {
- if ( $(this).is(":checked") ) {
- descendents.find( "input[type=checkbox]").attr( 'checked', true );
- } else {
- descendents.find( "input[type=checkbox]").attr( 'checked', false );
- // If you uncheck a lower level checkbox, uncheck the boxes above it
- // (since deselecting a child means the parent is not fully selected any
- // more).
- parents.children( "td" ).children( "input[type=checkbox]" ).attr( "checked", false );
- }
- });
- // return descendents for use by parent
- return descendents;
- }
- $(this).find( "tbody tr" ).not( "[parent]").each( function() {
- descendents = process_row( $(this), $([]) );
- descendents.hide();
- });
- });
- });
- function checkForm() {
- if ( $("select#action_on_datasets_select option:selected").text() == "delete" ) {
- if ( confirm( "Click OK to delete these datasets?" ) ) {
- return true;
- } else {
- return false;
- }
- }
- }
- // Looks for changes in dataset state using an async request. Keeps
- // calling itself (via setTimeout) until all datasets are in a terminal
- // state.
- var updater = function ( tracked_datasets ) {
- // Check if there are any items left to track
- var empty = true;
- for ( i in tracked_datasets ) {
- empty = false;
- break;
- }
- if ( ! empty ) {
- setTimeout( function() { updater_callback( tracked_datasets ) }, 3000 );
- }
- };
- var updater_callback = function ( tracked_datasets ) {
- // Build request data
- var ids = []
- var states = []
- $.each( tracked_datasets, function ( id, state ) {
- ids.push( id );
- states.push( state );
- });
- // Make ajax call
- $.ajax( {
- type: "POST",
- url: "${h.url_for( controller='library_common', action='library_item_updates' )}",
- dataType: "json",
- data: { ids: ids.join( "," ), states: states.join( "," ) },
- success : function ( data ) {
- $.each( data, function( id, val ) {
- // Replace HTML
- var cell = $("#libraryItem-" + id).find("#libraryItemInfo");
- cell.html( val.html );
- // If new state was terminal, stop tracking
- if (( val.state == "ok") || ( val.state == "error") || ( val.state == "empty") || ( val.state == "deleted" ) || ( val.state == "discarded" )) {
- delete tracked_datasets[ parseInt(id) ];
- } else {
- tracked_datasets[ parseInt(id) ] = val.state;
- }
- });
- updater( tracked_datasets );
- },
- error: function() {
- // Just retry, like the old method, should try to be smarter
- updater( tracked_datasets );
- }
- });
- };
-</script>
-
-<%def name="render_dataset( cntrller, ldda, library_dataset, selected, library, folder, pad, parent, row_conter, show_deleted=False )">
- <%
- ## The received data must always be a LibraryDatasetDatasetAssociation object. The object id passed to methods
- ## from the drop down menu should be the ldda id to prevent id collision ( which could happen when displaying
- ## children, which are always lddas ). We also need to make sure we're displaying the latest version of this
- ## library_dataset, so we display the attributes from the ldda.
- if ldda.user:
- uploaded_by = ldda.user.email
- else:
- uploaded_by = 'anonymous'
- if ldda == library_dataset.library_dataset_dataset_association:
- current_version = True
- if cntrller in [ 'library', 'requests' ]:
- can_modify = trans.app.security_agent.can_modify_library_item( current_user_roles, library_dataset )
- can_manage = trans.app.security_agent.can_manage_library_item( current_user_roles, library_dataset )
- else:
- current_version = False
- if current_version and ldda.state not in ( 'ok', 'error', 'empty', 'deleted', 'discarded' ):
- tracked_datasets[ldda.id] = ldda.state
- info_association, inherited = ldda.get_info_association( restrict=True )
- %>
- %if current_version and ( not ldda.library_dataset.deleted or show_deleted ):
- <tr class="datasetRow"
- %if parent is not None:
- parent="${parent}"
- style="display: none;"
- %endif
- id="libraryItem-${ldda.id}">
- <td style="padding-left: ${pad+20}px;">
- %if selected:
- <input type="checkbox" name="ldda_ids" value="${trans.security.encode_id( ldda.id )}" checked/>
- %else:
- <input type="checkbox" name="ldda_ids" value="${trans.security.encode_id( ldda.id )}"/>
- %endif
- %if ldda.library_dataset.deleted:
- <span class="libraryItem-error">
- %endif
- <a href="${h.url_for( controller='library_common', action='ldda_info', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), id=trans.security.encode_id( ldda.id ), show_deleted=show_deleted )}"><b>${ldda.name[:50]}</b></a>
- %if ldda.library_dataset.deleted:
- </span>
- %endif
- <a id="dataset-${ldda.id}-popup" class="popup-arrow" style="display: none;">▼</a>
- <div popupmenu="dataset-${ldda.id}-popup">
- %if not branch_deleted( folder ) and not ldda.library_dataset.deleted and ( cntrller == 'library_admin' or can_modify ):
- <a class="action-button" href="${h.url_for( controller='library_common', action='ldda_edit_info', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), id=trans.security.encode_id( ldda.id ), show_deleted=show_deleted )}">Edit information</a>
- %else:
- <a class="action-button" href="${h.url_for( controller='library_common', action='ldda_info', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), id=trans.security.encode_id( ldda.id ), show_deleted=show_deleted )}">View information</a>
- %endif
- %if not branch_deleted( folder ) and not ldda.library_dataset.deleted and ( ( cntrller == 'library_admin' or can_modify ) and not info_association ):
- <a class="action-button" href="${h.url_for( controller='library_common', action='add_template', cntrller=cntrller, item_type='ldda', library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), ldda_id=trans.security.encode_id( ldda.id ), show_deleted=show_deleted )}">Add template</a>
- %endif
- %if not branch_deleted( folder ) and not ldda.library_dataset.deleted and ( ( cntrller == 'library_admin' or can_modify ) and info_association ):
- <a class="action-button" href="${h.url_for( controller='library_common', action='edit_template', cntrller=cntrller, item_type='ldda', library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), ldda_id=trans.security.encode_id( ldda.id ), show_deleted=show_deleted )}">Edit template</a>
- <a class="action-button" href="${h.url_for( controller='library_common', action='delete_template', cntrller=cntrller, item_type='ldda', library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), ldda_id=trans.security.encode_id( ldda.id ), show_deleted=show_deleted )}">Delete template</a>
- %endif
- %if not branch_deleted( folder ) and not ldda.library_dataset.deleted and ( cntrller == 'library_admin' or can_manage ):
- <a class="action-button" href="${h.url_for( controller='library_common', action='ldda_permissions', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), id=trans.security.encode_id( ldda.id ), show_deleted=show_deleted )}">Edit permissions</a>
- %endif
- %if not branch_deleted( folder ) and not ldda.library_dataset.deleted and ( cntrller == 'library_admin' or can_modify ):
- <a class="action-button" href="${h.url_for( controller='library_common', action='upload_library_dataset', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), replace_id=trans.security.encode_id( library_dataset.id ), show_deleted=show_deleted )}">Upload a new version of this dataset</a>
- %endif
- %if not branch_deleted( folder ) and not ldda.library_dataset.deleted and ldda.has_data:
- <a class="action-button" href="${h.url_for( controller='library_common', action='act_on_multiple_datasets', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), ldda_ids=trans.security.encode_id( ldda.id ), do_action='add', show_deleted=show_deleted )}">Import this dataset into your current history</a>
- <a class="action-button" href="${h.url_for( controller='library_common', action='download_dataset_from_folder', cntrller=cntrller, id=trans.security.encode_id( ldda.id ), library_id=trans.security.encode_id( library.id ) )}">Download this dataset</a>
- %endif
- %if cntrller in [ 'library_admin', 'requests_admin' ]:
- %if not library.deleted and not branch_deleted( folder ) and not ldda.library_dataset.deleted:
- <a class="action-button" confirm="Click OK to delete dataset '${ldda.name}'." href="${h.url_for( controller='library_admin', action='delete_library_item', library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library_dataset.id ), item_type='library_dataset', show_deleted=show_deleted )}">Delete this dataset</a>
- %elif not library.deleted and not branch_deleted( folder ) and not ldda.library_dataset.purged and ldda.library_dataset.deleted:
- <a class="action-button" href="${h.url_for( controller='library_admin', action='undelete_library_item', library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library_dataset.id ), item_type='library_dataset', show_deleted=show_deleted )}">Undelete this dataset</a>
- %endif
- %endif
- </div>
- </td>
- <td id="libraryItemInfo">${render_library_item_info( ldda )}</td>
- <td>${uploaded_by}</td>
- <td>${ldda.create_time.strftime( "%Y-%m-%d" )}</td>
- </tr>
- <%
- my_row = row_counter.count
- row_counter.increment()
- %>
- %endif
+<%def name="javascripts()">
+ ${parent.javascripts()}
+ ${self.grid_javascripts()}
</%def>
-<%def name="render_folder( cntrller, folder, folder_pad, created_ldda_ids, library_id, hidden_folder_ids, show_deleted=False, parent=None, row_counter=None, root_folder=False )">
- <%
- if root_folder:
- pad = folder_pad
- expander = "/static/images/silk/resultset_bottom.png"
- folder_img = "/static/images/silk/folder_page.png"
- else:
- pad = folder_pad + 20
- expander = "/static/images/silk/resultset_next.png"
- folder_img = "/static/images/silk/folder.png"
- if created_ldda_ids:
- created_ldda_ids = util.listify( created_ldda_ids )
- if str( folder.id ) in hidden_folder_ids:
- return ""
- my_row = None
- if cntrller in [ 'library', 'requests' ]:
- can_access, folder_ids = trans.app.security_agent.check_folder_contents( trans.user, current_user_roles, folder )
- if not can_access:
- can_show, folder_ids = \
- trans.app.security_agent.show_library_item( trans.user,
- current_user_roles,
- folder,
- [ trans.app.security_agent.permitted_actions.LIBRARY_ADD,
- trans.app.security_agent.permitted_actions.LIBRARY_MODIFY,
- trans.app.security_agent.permitted_actions.LIBRARY_MANAGE ] )
- if not can_show:
- return ""
- can_add = trans.app.security_agent.can_add_library_item( current_user_roles, folder )
- can_modify = trans.app.security_agent.can_modify_library_item( current_user_roles, folder )
- can_manage = trans.app.security_agent.can_manage_library_item( current_user_roles, folder )
- info_association, inherited = folder.get_info_association( restrict=True )
- %>
- %if not root_folder and ( not folder.deleted or show_deleted ):
- <tr class="folderRow libraryOrFolderRow"
- %if parent is not None:
- parent="${parent}"
- style="display: none;"
- %endif
- >
- <td style="padding-left: ${folder_pad}px;">
- <span class="expandLink"></span>
- <input type="checkbox" class="folderCheckbox"/>
- <span class="rowIcon"></span>
- %if folder.deleted:
- <span class="libraryItem-error">
- %endif
- <a href="${h.url_for( controller='library_common', action='folder_info', cntrller=cntrller, id=trans.security.encode_id( folder.id ), library_id=library_id, show_deleted=show_deleted )}">${folder.name}</a>
- %if folder.description:
- <i>- ${folder.description}</i>
- %endif
- %if folder.deleted:
- </span>
- %endif
- <a id="folder_img-${folder.id}-popup" class="popup-arrow" style="display: none;">▼</a>
- <div popupmenu="folder_img-${folder.id}-popup">
- %if not branch_deleted( folder ) and ( cntrller == 'library_admin' or can_add ):
- <a class="action-button" href="${h.url_for( controller='library_common', action='upload_library_dataset', cntrller=cntrller, library_id=library_id, folder_id=trans.security.encode_id( folder.id ), show_deleted=show_deleted )}">Add datasets</a>
- <a class="action-button" href="${h.url_for( controller='library_common', action='create_folder', cntrller=cntrller, parent_id=trans.security.encode_id( folder.id ), library_id=library_id )}">Add sub-folder</a>
- %endif
- %if not branch_deleted( folder ) and ( cntrller == 'library_admin' or can_modify ):
- <a class="action-button" href="${h.url_for( controller='library_common', action='folder_info', cntrller=cntrller, id=trans.security.encode_id( folder.id ), library_id=library_id, show_deleted=show_deleted )}">Edit information</a>
- %endif
- %if not branch_deleted( folder ) and ( ( cntrller == 'library_admin' or can_modify ) and not info_association ):
- <a class="action-button" href="${h.url_for( controller='library_common', action='add_template', cntrller=cntrller, item_type='folder', library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), show_deleted=show_deleted )}">Add template</a>
- %endif
- %if not branch_deleted( folder ) and ( ( cntrller == 'library_admin' or can_modify ) and info_association ):
- <a class="action-button" href="${h.url_for( controller='library_common', action='edit_template', cntrller=cntrller, item_type='folder', library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), show_deleted=show_deleted )}">Edit template</a>
- <a class="action-button" href="${h.url_for( controller='library_common', action='delete_template', cntrller=cntrller, item_type='folder', library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), show_deleted=show_deleted )}">Delete template</a>
- %endif
- %if not branch_deleted( folder ) and ( cntrller == 'library_admin' or can_manage ):
- <a class="action-button" href="${h.url_for( controller='library_common', action='folder_permissions', cntrller=cntrller, id=trans.security.encode_id( folder.id ), library_id=library_id, show_deleted=show_deleted )}">Edit permissions</a>
- %endif
- %if cntrller in [ 'library_admin', 'requests_admin' ]:
- %if not library.deleted and not folder.deleted:
- <a class="action-button" confirm="Click OK to delete the folder '${folder.name}.'" href="${h.url_for( controller='library_admin', action='delete_library_item', library_id=library_id, item_id=trans.security.encode_id( folder.id ), item_type='folder', show_deleted=show_deleted )}">Delete this folder</a>
- %elif not library.deleted and folder.deleted and not folder.purged:
- <a class="action-button" href="${h.url_for( controller='library_admin', action='undelete_library_item', library_id=library_id, item_id=trans.security.encode_id( folder.id ), item_type='folder', show_deleted=show_deleted )}">Undelete this folder</a>
- %endif
- %endif
- </div>
- </div>
- <td colspan="3"></td>
- </tr>
- <%
- my_row = row_counter.count
- row_counter.increment()
- %>
- %endif
- %if cntrller == 'library':
- <% sub_folders = active_folders( trans, folder ) %>
- %for sub_folder in sub_folders:
- ${render_folder( cntrller, sub_folder, pad, created_ldda_ids, library_id, hidden_folder_ids, parent=my_row, row_counter=row_counter )}
- %endfor
- %for library_dataset in folder.active_library_datasets:
- <%
- ldda = library_dataset.library_dataset_dataset_association
- can_access = trans.app.security_agent.can_access_dataset( current_user_roles, ldda.dataset )
- selected = created_ldda_ids and str( ldda.id ) in created_ldda_ids
- %>
- %if can_access:
- ${render_dataset( cntrller, ldda, library_dataset, selected, library, folder, pad, my_row, row_counter )}
- %endif
- %endfor
- %elif cntrller == 'library_admin':
- <%
- if show_deleted:
- sub_folders, lddas = activatable_folders_and_lddas( trans, folder )
- else:
- sub_folders, lddas = active_folders_and_lddas( trans, folder )
- %>
- %for sub_folder in sub_folders:
- ${render_folder( cntrller, sub_folder, pad, created_ldda_ids, library_id, [], parent=my_row, row_counter=row_counter, show_deleted=show_deleted )}
- %endfor
- %for ldda in lddas:
- <%
- library_dataset = ldda.library_dataset
- selected = created_ldda_ids and str( ldda.id ) in created_ldda_ids
- %>
- ${render_dataset( cntrller, ldda, library_dataset, selected, library, folder, pad, my_row, row_counter, show_deleted=show_deleted )}
- %endfor
- %endif
+<%def name="grid_javascripts()">
+ <script type="text/javascript">
+ $( document ).ready( function () {
+ $("#library-grid").each( function() {
+ // Recursively fill in children and descendents of each row
+ var process_row = function( q, parents ) {
+ // Find my index
+ var index = $(q).parent().children().index( $(q) );
+ // Find my immediate children
+ var children = $(q).siblings().filter( "[parent='" + index + "']" );
+ // Recursively handle them
+ var descendents = children;
+ children.each( function() {
+ child_descendents = process_row( $(this), parents.add( q ) );
+ descendents = descendents.add( child_descendents );
+ });
+ // Set up expand / hide link
+ // HACK: assume descendents are invisible. The caller actually
+ // ensures this for the root node. However, if we start
+ // remembering folder states, we'll need something
+ // more sophisticated here.
+ var visible = false;
+ $(q).find( "span.expandLink").click( function() {
+ if ( visible ) {
+ descendents.hide();
+ descendents.removeClass( "expanded" );
+ q.removeClass( "expanded" );
+ visible = false;
+ } else {
+ children.show();
+ q.addClass( "expanded" );
+ visible = true;
+ }
+ });
+ // Check/uncheck boxes in subfolders.
+ q.children( "td" ).children( "input[type=checkbox]" ).click( function() {
+ if ( $(this).is(":checked") ) {
+ descendents.find( "input[type=checkbox]").attr( 'checked', true );
+ } else {
+ descendents.find( "input[type=checkbox]").attr( 'checked', false );
+ // If you uncheck a lower level checkbox, uncheck the boxes above it
+ // (since deselecting a child means the parent is not fully selected any
+ // more).
+ parents.children( "td" ).children( "input[type=checkbox]" ).attr( "checked", false );
+ }
+ });
+ // return descendents for use by parent
+ return descendents;
+ }
+ $(this).find( "tbody tr" ).not( "[parent]").each( function() {
+ descendents = process_row( $(this), $([]) );
+ descendents.hide();
+ });
+ });
+ });
+ function checkForm() {
+ if ( $("select#action_on_datasets_select option:selected").text() == "delete" ) {
+ if ( confirm( "Click OK to delete these datasets?" ) ) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+ // Looks for changes in dataset state using an async request. Keeps
+ // calling itself (via setTimeout) until all datasets are in a terminal
+ // state.
+ var updater = function ( tracked_datasets ) {
+ // Check if there are any items left to track
+ var empty = true;
+ for ( i in tracked_datasets ) {
+ empty = false;
+ break;
+ }
+ if ( ! empty ) {
+ setTimeout( function() { updater_callback( tracked_datasets ) }, 3000 );
+ }
+ };
+ var updater_callback = function ( tracked_datasets ) {
+ // Build request data
+ var ids = []
+ var states = []
+ $.each( tracked_datasets, function ( id, state ) {
+ ids.push( id );
+ states.push( state );
+ });
+ // Make ajax call
+ $.ajax( {
+ type: "POST",
+ url: "${h.url_for( controller='library_common', action='library_item_updates' )}",
+ dataType: "json",
+ data: { ids: ids.join( "," ), states: states.join( "," ) },
+ success : function ( data ) {
+ $.each( data, function( id, val ) {
+ // Replace HTML
+ var cell = $("#libraryItem-" + id).find("#libraryItemInfo");
+ cell.html( val.html );
+ // If new state was terminal, stop tracking
+ if (( val.state == "ok") || ( val.state == "error") || ( val.state == "empty") || ( val.state == "deleted" ) || ( val.state == "discarded" )) {
+ delete tracked_datasets[ parseInt(id) ];
+ } else {
+ tracked_datasets[ parseInt(id) ] = val.state;
+ }
+ });
+ updater( tracked_datasets );
+ },
+ error: function() {
+ // Just retry, like the old method, should try to be smarter
+ updater( tracked_datasets );
+ }
+ });
+ };
+ </script>
</%def>
-<h2>Data Library “${library.name}”</h2>
+<%def name="render_dataset( cntrller, ldda, library_dataset, selected, library, folder, pad, parent, row_counter, tracked_datasets, show_deleted=False )">
+ <%
+ ## The received data must always be a LibraryDatasetDatasetAssociation object. The object id passed to methods
+ ## from the drop down menu should be the ldda id to prevent id collision ( which could happen when displaying
+ ## children, which are always lddas ). We also need to make sure we're displaying the latest version of this
+ ## library_dataset, so we display the attributes from the ldda.
-<ul class="manage-table-actions">
- %if not library.deleted and ( cntrller in [ 'library_admin', 'requests_admin' ] or can_add ):
- <li><a class="action-button" href="${h.url_for( controller='library_common', action='upload_library_dataset', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( library.root_folder.id ), show_deleted=show_deleted )}"><span>Add datasets</span></a></li>
- <li><a class="action-button" href="${h.url_for( controller='library_common', action='create_folder', cntrller=cntrller, parent_id=trans.security.encode_id( library.root_folder.id ), library_id=trans.security.encode_id( library.id ), show_deleted=show_deleted )}">Add folder</a></li>
- %endif
-</ul>
+ from galaxy.web.controllers.library_common import active_folders, active_folders_and_lddas, activatable_folders_and_lddas, branch_deleted
-%if msg:
- ${render_msg( msg, messagetype )}
-%endif
+ if ldda.user:
+ uploaded_by = ldda.user.email
+ else:
+ uploaded_by = 'anonymous'
+ if ldda == library_dataset.library_dataset_dataset_association:
+ current_version = True
+ if cntrller in [ 'library', 'requests' ]:
+ can_modify = trans.app.security_agent.can_modify_library_item( current_user_roles, library_dataset )
+ can_manage = trans.app.security_agent.can_manage_library_item( current_user_roles, library_dataset )
+ else:
+ current_version = False
+ if current_version and ldda.state not in ( 'ok', 'error', 'empty', 'deleted', 'discarded' ):
+ tracked_datasets[ldda.id] = ldda.state
+ info_association, inherited = ldda.get_info_association( restrict=True )
+ %>
+ %if current_version and ( not ldda.library_dataset.deleted or show_deleted ):
+ <tr class="datasetRow"
+ %if parent is not None:
+ parent="${parent}"
+ style="display: none;"
+ %endif
+ id="libraryItem-${ldda.id}">
+ <td style="padding-left: ${pad+20}px;">
+ %if selected:
+ <input type="checkbox" name="ldda_ids" value="${trans.security.encode_id( ldda.id )}" checked/>
+ %else:
+ <input type="checkbox" name="ldda_ids" value="${trans.security.encode_id( ldda.id )}"/>
+ %endif
+ %if ldda.library_dataset.deleted:
+ <span class="libraryItem-error">
+ %endif
+ <a href="${h.url_for( controller='library_common', action='ldda_info', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), id=trans.security.encode_id( ldda.id ), show_deleted=show_deleted )}"><b>${ldda.name[:50]}</b></a>
+ %if ldda.library_dataset.deleted:
+ </span>
+ %endif
+ <a id="dataset-${ldda.id}-popup" class="popup-arrow" style="display: none;">▼</a>
+ <div popupmenu="dataset-${ldda.id}-popup">
+ %if not branch_deleted( folder ) and not ldda.library_dataset.deleted and ( cntrller == 'library_admin' or can_modify ):
+ <a class="action-button" href="${h.url_for( controller='library_common', action='ldda_edit_info', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), id=trans.security.encode_id( ldda.id ), show_deleted=show_deleted )}">Edit information</a>
+ %else:
+ <a class="action-button" href="${h.url_for( controller='library_common', action='ldda_info', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), id=trans.security.encode_id( ldda.id ), show_deleted=show_deleted )}">View information</a>
+ %endif
+ %if not branch_deleted( folder ) and not ldda.library_dataset.deleted and ( ( cntrller == 'library_admin' or can_modify ) and not info_association ):
+ <a class="action-button" href="${h.url_for( controller='library_common', action='add_template', cntrller=cntrller, item_type='ldda', library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), ldda_id=trans.security.encode_id( ldda.id ), show_deleted=show_deleted )}">Add template</a>
+ %endif
+ %if not branch_deleted( folder ) and not ldda.library_dataset.deleted and ( ( cntrller == 'library_admin' or can_modify ) and info_association ):
+ <a class="action-button" href="${h.url_for( controller='library_common', action='edit_template', cntrller=cntrller, item_type='ldda', library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), ldda_id=trans.security.encode_id( ldda.id ), show_deleted=show_deleted )}">Edit template</a>
+ <a class="action-button" href="${h.url_for( controller='library_common', action='delete_template', cntrller=cntrller, item_type='ldda', library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), ldda_id=trans.security.encode_id( ldda.id ), show_deleted=show_deleted )}">Delete template</a>
+ %endif
+ %if not branch_deleted( folder ) and not ldda.library_dataset.deleted and ( cntrller == 'library_admin' or can_manage ):
+ <a class="action-button" href="${h.url_for( controller='library_common', action='ldda_permissions', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), id=trans.security.encode_id( ldda.id ), show_deleted=show_deleted )}">Edit permissions</a>
+ %endif
+ %if not branch_deleted( folder ) and not ldda.library_dataset.deleted and ( cntrller == 'library_admin' or can_modify ):
+ <a class="action-button" href="${h.url_for( controller='library_common', action='upload_library_dataset', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), replace_id=trans.security.encode_id( library_dataset.id ), show_deleted=show_deleted )}">Upload a new version of this dataset</a>
+ %endif
+ %if not branch_deleted( folder ) and not ldda.library_dataset.deleted and ldda.has_data:
+ <a class="action-button" href="${h.url_for( controller='library_common', action='act_on_multiple_datasets', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), ldda_ids=trans.security.encode_id( ldda.id ), do_action='add', show_deleted=show_deleted )}">Import this dataset into your current history</a>
+ <a class="action-button" href="${h.url_for( controller='library_common', action='download_dataset_from_folder', cntrller=cntrller, id=trans.security.encode_id( ldda.id ), library_id=trans.security.encode_id( library.id ) )}">Download this dataset</a>
+ %endif
+ %if cntrller in [ 'library_admin', 'requests_admin' ]:
+ %if not library.deleted and not branch_deleted( folder ) and not ldda.library_dataset.deleted:
+ <a class="action-button" confirm="Click OK to delete dataset '${ldda.name}'." href="${h.url_for( controller='library_admin', action='delete_library_item', library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library_dataset.id ), item_type='library_dataset', show_deleted=show_deleted )}">Delete this dataset</a>
+ %elif not library.deleted and not branch_deleted( folder ) and not ldda.library_dataset.purged and ldda.library_dataset.deleted:
+ <a class="action-button" href="${h.url_for( controller='library_admin', action='undelete_library_item', library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library_dataset.id ), item_type='library_dataset', show_deleted=show_deleted )}">Undelete this dataset</a>
+ %endif
+ %endif
+ </div>
+ </td>
+ <td id="libraryItemInfo">${render_library_item_info( ldda )}</td>
+ <td>${uploaded_by}</td>
+ <td>${ldda.create_time.strftime( "%Y-%m-%d" )}</td>
+ </tr>
+ <%
+ my_row = row_counter.count
+ row_counter.increment()
+ %>
+ %endif
+</%def>
-%if library.description:
- <div class="libraryItemBody">${library.description}</div>
- <br/>
-%endif
+<%def name="render_folder( cntrller, folder, folder_pad, created_ldda_ids, library_id, hidden_folder_ids, tracked_datasets, show_deleted=False, parent=None, row_counter=None, root_folder=False )">
+ <%
+ from galaxy.web.controllers.library_common import active_folders, active_folders_and_lddas, activatable_folders_and_lddas, branch_deleted
-<form name="act_on_multiple_datasets" action="${h.url_for( controller='library_common', action='act_on_multiple_datasets', cntrller=cntrller, library_id=trans.security.encode_id( library.id ) )}" onSubmit="javascript:return checkForm();" method="post">
- <table cellspacing="0" cellpadding="0" border="0" width="100%" class="grid" id="library-grid">
- <thead>
- <tr class="libraryTitle">
- %if cntrller == 'library_admin' or can_add or can_modify or can_manage:
- <th style="padding-left: 42px;">
- <a href="${h.url_for( controller='library_common', action='library_info', cntrller=cntrller, id=trans.security.encode_id( library.id ), show_deleted=show_deleted )}"><b>${library.name[:50]}</b></a>
- <a id="library-${library.id}-popup" class="popup-arrow" style="display: none;">▼</a>
- <div popupmenu="library-${library.id}-popup">
- %if not library.deleted:
- %if cntrller == 'library_admin' or can_modify:
- <a class="action-button" href="${h.url_for( controller='library_common', action='library_info', cntrller=cntrller, id=trans.security.encode_id( library.id ), show_deleted=show_deleted )}">Edit information</a>
- %endif
- %if ( cntrller == 'library_admin' or can_modify ) and not library.info_association:
- <a class="action-button" href="${h.url_for( controller='library_common', action='add_template', cntrller=cntrller, item_type='library', library_id=trans.security.encode_id( library.id ), show_deleted=show_deleted )}">Add template</a>
- %endif
- %if ( cntrller == 'library_admin' or can_modify ) and info_association:
- <a class="action-button" href="${h.url_for( controller='library_common', action='edit_template', cntrller=cntrller, item_type='library', library_id=trans.security.encode_id( library.id ), show_deleted=show_deleted )}">Edit template</a>
- <a class="action-button" href="${h.url_for( controller='library_common', action='delete_template', cntrller=cntrller, item_type='library', library_id=trans.security.encode_id( library.id ), show_deleted=show_deleted )}">Delete template</a>
- %endif
- %if cntrller == 'library_admin' or can_manage:
- <a class="action-button" href="${h.url_for( controller='library_common', action='library_permissions', cntrller=cntrller, id=trans.security.encode_id( library.id ), show_deleted=show_deleted )}">Edit permissions</a>
- %endif
- %if cntrller == 'library_admin':
- <a class="action-button" confirm="Click OK to delete the library named '${library.name}'." href="${h.url_for( controller='library_admin', action='delete_library_item', library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library.id ), item_type='library' )}">Delete this data library</a>
- %endif
- %if show_deleted:
- <a class="action-button" href="${h.url_for( controller='library_common', action='browse_library', cntrller=cntrller, id=trans.security.encode_id( library.id ), show_deleted=False )}">Hide deleted items</a>
- %else:
- <a class="action-button" href="${h.url_for( controller='library_common', action='browse_library', cntrller=cntrller, id=trans.security.encode_id( library.id ), show_deleted=True )}">Show deleted items</a>
- %endif
- %elif cntrller == 'library_admin' and not library.purged:
- <a class="action-button" href="${h.url_for( controller='library_admin', action='undelete_library_item', library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library.id ), item_type='library' )}">Undelete this data library</a>
- %elif library.purged:
- <a class="action-button" href="${h.url_for( controller='library_common', action='browse_library', cntrller=cntrller, id=trans.security.encode_id( library.id ), show_deleted=show_deleted )}">This data library has been purged</a>
- %endif
- </div>
- </th>
- %else:
- <th style="padding-left: 42px;">
- <a href="${h.url_for( controller='library_common', action='library_info', cntrller=cntrller, id=trans.security.encode_id( library.id ), show_deleted=show_deleted )}"><b>${library.name[:50]}</b></a>
- </th>
- %endif
- <th>Information</th>
- <th>Uploaded By</th>
- <th>Date</th>
- </thead>
- </tr>
- <% row_counter = RowCounter() %>
- %if cntrller in [ 'library', 'requests' ]:
- ${render_folder( 'library', library.root_folder, 0, created_ldda_ids, trans.security.encode_id( library.id ), hidden_folder_ids, parent=None, row_counter=row_counter, root_folder=True )}
- %if not library.deleted:
- ${render_actions_on_multiple_items( 'library', default_action=default_action )}
- %endif
- %elif cntrller in [ 'library_admin', 'requests_admin' ]:
- ${render_folder( 'library_admin', library.root_folder, 0, created_ldda_ids, trans.security.encode_id( library.id ), [], parent=None, row_counter=row_counter, root_folder=True, show_deleted=show_deleted )}
- %if not library.deleted and not show_deleted:
- ${render_actions_on_multiple_items( 'library_admin' )}
- %endif
- %endif
- </table>
-</form>
+ if root_folder:
+ pad = folder_pad
+ expander = "/static/images/silk/resultset_bottom.png"
+ folder_img = "/static/images/silk/folder_page.png"
+ else:
+ pad = folder_pad + 20
+ expander = "/static/images/silk/resultset_next.png"
+ folder_img = "/static/images/silk/folder.png"
+ if created_ldda_ids:
+ created_ldda_ids = util.listify( created_ldda_ids )
+ if str( folder.id ) in hidden_folder_ids:
+ return ""
+ my_row = None
+ if cntrller in [ 'library', 'requests' ]:
+ can_access, folder_ids = trans.app.security_agent.check_folder_contents( trans.user, current_user_roles, folder )
+ if not can_access:
+ can_show, folder_ids = \
+ trans.app.security_agent.show_library_item( trans.user,
+ current_user_roles,
+ folder,
+ [ trans.app.security_agent.permitted_actions.LIBRARY_ADD,
+ trans.app.security_agent.permitted_actions.LIBRARY_MODIFY,
+ trans.app.security_agent.permitted_actions.LIBRARY_MANAGE ] )
+ if not can_show:
+ return ""
+ can_add = trans.app.security_agent.can_add_library_item( current_user_roles, folder )
+ can_modify = trans.app.security_agent.can_modify_library_item( current_user_roles, folder )
+ can_manage = trans.app.security_agent.can_manage_library_item( current_user_roles, folder )
+ info_association, inherited = folder.get_info_association( restrict=True )
+ %>
+ %if not root_folder and ( not folder.deleted or show_deleted ):
+ <tr class="folderRow libraryOrFolderRow"
+ %if parent is not None:
+ parent="${parent}"
+ style="display: none;"
+ %endif
-%if tracked_datasets:
- <script type="text/javascript">
- // Updater
- updater({${ ",".join( [ '"%s" : "%s"' % ( k, v ) for k, v in tracked_datasets.iteritems() ] ) }});
- </script>
- <!-- running: do not change this comment, used by TwillTestCase.library_wait -->
-%endif
+ <td style="padding-left: ${folder_pad}px;">
+ <span class="expandLink"></span>
+ <input type="checkbox" class="folderCheckbox"/>
+ <span class="rowIcon"></span>
+ %if folder.deleted:
+ <span class="libraryItem-error">
+ %endif
+ <a href="${h.url_for( controller='library_common', action='folder_info', cntrller=cntrller, id=trans.security.encode_id( folder.id ), library_id=library_id, show_deleted=show_deleted )}">${folder.name}</a>
+ %if folder.description:
+ <i>- ${folder.description}</i>
+ %endif
+ %if folder.deleted:
+ </span>
+ %endif
+ <a id="folder_img-${folder.id}-popup" class="popup-arrow" style="display: none;">▼</a>
+ <div popupmenu="folder_img-${folder.id}-popup">
+ %if not branch_deleted( folder ) and ( cntrller == 'library_admin' or can_add ):
+ <a class="action-button" href="${h.url_for( controller='library_common', action='upload_library_dataset', cntrller=cntrller, library_id=library_id, folder_id=trans.security.encode_id( folder.id ), show_deleted=show_deleted )}">Add datasets</a>
+ <a class="action-button" href="${h.url_for( controller='library_common', action='create_folder', cntrller=cntrller, parent_id=trans.security.encode_id( folder.id ), library_id=library_id )}">Add sub-folder</a>
+ %endif
+ %if not branch_deleted( folder ) and ( cntrller == 'library_admin' or can_modify ):
+ <a class="action-button" href="${h.url_for( controller='library_common', action='folder_info', cntrller=cntrller, id=trans.security.encode_id( folder.id ), library_id=library_id, show_deleted=show_deleted )}">Edit information</a>
+ %endif
+ %if not branch_deleted( folder ) and ( ( cntrller == 'library_admin' or can_modify ) and not info_association ):
+ <a class="action-button" href="${h.url_for( controller='library_common', action='add_template', cntrller=cntrller, item_type='folder', library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), show_deleted=show_deleted )}">Add template</a>
+ %endif
+ %if not branch_deleted( folder ) and ( ( cntrller == 'library_admin' or can_modify ) and info_association ):
+ <a class="action-button" href="${h.url_for( controller='library_common', action='edit_template', cntrller=cntrller, item_type='folder', library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), show_deleted=show_deleted )}">Edit template</a>
+ <a class="action-button" href="${h.url_for( controller='library_common', action='delete_template', cntrller=cntrller, item_type='folder', library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), show_deleted=show_deleted )}">Delete template</a>
+ %endif
+ %if not branch_deleted( folder ) and ( cntrller == 'library_admin' or can_manage ):
+ <a class="action-button" href="${h.url_for( controller='library_common', action='folder_permissions', cntrller=cntrller, id=trans.security.encode_id( folder.id ), library_id=library_id, show_deleted=show_deleted )}">Edit permissions</a>
+ %endif
+ %if cntrller in [ 'library_admin', 'requests_admin' ]:
+ %if not library.deleted and not folder.deleted:
+ <a class="action-button" confirm="Click OK to delete the folder '${folder.name}.'" href="${h.url_for( controller='library_admin', action='delete_library_item', library_id=library_id, item_id=trans.security.encode_id( folder.id ), item_type='folder', show_deleted=show_deleted )}">Delete this folder</a>
+ %elif not library.deleted and folder.deleted and not folder.purged:
+ <a class="action-button" href="${h.url_for( controller='library_admin', action='undelete_library_item', library_id=library_id, item_id=trans.security.encode_id( folder.id ), item_type='folder', show_deleted=show_deleted )}">Undelete this folder</a>
+ %endif
+ %endif
+ </div>
+ </div>
+ <td colspan="3"></td>
+ </tr>
+ <%
+ my_row = row_counter.count
+ row_counter.increment()
+ %>
+ %endif
+ %if cntrller == 'library':
+ <% sub_folders = active_folders( trans, folder ) %>
+ %for sub_folder in sub_folders:
+ ${render_folder( cntrller, sub_folder, pad, created_ldda_ids, library_id, hidden_folder_ids, tracked_datasets, show_deleted=show_deleted, parent=my_row, row_counter=row_counter, root_folder=False )}
+ %endfor
+ %for library_dataset in folder.active_library_datasets:
+ <%
+ ldda = library_dataset.library_dataset_dataset_association
+ can_access = trans.app.security_agent.can_access_dataset( current_user_roles, ldda.dataset )
+ selected = created_ldda_ids and str( ldda.id ) in created_ldda_ids
+ %>
+ %if can_access:
+ ${render_dataset( cntrller, ldda, library_dataset, selected, library, folder, pad, my_row, row_counter, tracked_datasets, show_deleted=show_deleted )}
+ %endif
+ %endfor
+ %elif cntrller == 'library_admin':
+ <%
+ if show_deleted:
+ sub_folders, lddas = activatable_folders_and_lddas( trans, folder )
+ else:
+ sub_folders, lddas = active_folders_and_lddas( trans, folder )
+ %>
+ %for sub_folder in sub_folders:
+ ${render_folder( cntrller, sub_folder, pad, created_ldda_ids, library_id, [], tracked_datasets, show_deleted=show_deleted, parent=my_row, row_counter=row_counter, root_folder=False )}
+ %endfor
+ %for ldda in lddas:
+ <%
+ library_dataset = ldda.library_dataset
+ selected = created_ldda_ids and str( ldda.id ) in created_ldda_ids
+ %>
+ ${render_dataset( cntrller, ldda, library_dataset, selected, library, folder, pad, my_row, row_counter, tracked_datasets, show_deleted=show_deleted )}
+ %endfor
+ %endif
+</%def>
-## Help about compression types
+<%def name="render_content()">
+ <%
+ from galaxy import util
+ from galaxy.web.controllers.library_common import branch_deleted
+ from time import strftime
-%if len( comptypes ) > 1:
- <div>
- <p class="infomark">
- TIP: Multiple compression options are available for downloading library datasets:
- </p>
- <ul style="padding-left: 1em; list-style-type: disc;">
- %if 'bz2' in comptypes:
- <li>bzip2: Compression takes the most time but is better for slower network connections (that transfer slower than the rate of compression) since the resulting file size is smallest.</li>
- %endif
- %if 'gz' in comptypes:
- <li>gzip: Compression is faster and yields a larger file, making it more suitable for fast network connections.</li>
- %endif
- %if 'zip' in comptypes:
- <li>ZIP: Not recommended but is provided as an option for those on Windows without WinZip (since WinZip can read .bz2 and .gz files).</li>
- %endif
- </ul>
- </div>
-%endif
+ if cntrller in [ 'library', 'requests' ]:
+ can_add = trans.app.security_agent.can_add_library_item( current_user_roles, library )
+ can_modify = trans.app.security_agent.can_modify_library_item( current_user_roles, library )
+ can_manage = trans.app.security_agent.can_manage_library_item( current_user_roles, library )
+ info_association, inherited = library.get_info_association()
+
+ tracked_datasets = {}
+
+ class RowCounter( object ):
+ def __init__( self ):
+ self.count = 0
+ def increment( self ):
+ self.count += 1
+ def __str__( self ):
+ return str( self.count )
+ %>
+
+ <h2>Data Library “${library.name}”</h2>
+
+ <ul class="manage-table-actions">
+ %if not library.deleted and ( cntrller in [ 'library_admin', 'requests_admin' ] or can_add ):
+ <li><a class="action-button" href="${h.url_for( controller='library_common', action='upload_library_dataset', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( library.root_folder.id ), show_deleted=show_deleted )}"><span>Add datasets</span></a></li>
+ <li><a class="action-button" href="${h.url_for( controller='library_common', action='create_folder', cntrller=cntrller, parent_id=trans.security.encode_id( library.root_folder.id ), library_id=trans.security.encode_id( library.id ), show_deleted=show_deleted )}">Add folder</a></li>
+ %endif
+ </ul>
+
+ %if msg:
+ ${render_msg( msg, messagetype )}
+ %endif
+
+ <div class="libraryItemBody">
+ ${library.synopsis}
+ </div>
+ <br/>
+
+ <form name="act_on_multiple_datasets" action="${h.url_for( controller='library_common', action='act_on_multiple_datasets', cntrller=cntrller, library_id=trans.security.encode_id( library.id ) )}" onSubmit="javascript:return checkForm();" method="post">
+ <table cellspacing="0" cellpadding="0" border="0" width="100%" class="grid" id="library-grid">
+ <thead>
+ <tr class="libraryTitle">
+ %if cntrller == 'library_admin' or can_add or can_modify or can_manage:
+ <th style="padding-left: 42px;">
+ <a href="${h.url_for( controller='library_common', action='library_info', cntrller=cntrller, id=trans.security.encode_id( library.id ), show_deleted=show_deleted )}"><b>${library.name[:50]}</b></a>
+ <a id="library-${library.id}-popup" class="popup-arrow" style="display: none;">▼</a>
+ <div popupmenu="library-${library.id}-popup">
+ %if not library.deleted:
+ %if cntrller == 'library_admin' or can_modify:
+ <a class="action-button" href="${h.url_for( controller='library_common', action='library_info', cntrller=cntrller, id=trans.security.encode_id( library.id ), show_deleted=show_deleted )}">Edit information</a>
+ %endif
+ %if ( cntrller == 'library_admin' or can_modify ) and not library.info_association:
+ <a class="action-button" href="${h.url_for( controller='library_common', action='add_template', cntrller=cntrller, item_type='library', library_id=trans.security.encode_id( library.id ), show_deleted=show_deleted )}">Add template</a>
+ %endif
+ %if ( cntrller == 'library_admin' or can_modify ) and info_association:
+ <a class="action-button" href="${h.url_for( controller='library_common', action='edit_template', cntrller=cntrller, item_type='library', library_id=trans.security.encode_id( library.id ), show_deleted=show_deleted )}">Edit template</a>
+ <a class="action-button" href="${h.url_for( controller='library_common', action='delete_template', cntrller=cntrller, item_type='library', library_id=trans.security.encode_id( library.id ), show_deleted=show_deleted )}">Delete template</a>
+ %endif
+ %if cntrller == 'library_admin' or can_manage:
+ <a class="action-button" href="${h.url_for( controller='library_common', action='library_permissions', cntrller=cntrller, id=trans.security.encode_id( library.id ), show_deleted=show_deleted )}">Edit permissions</a>
+ %endif
+ %if cntrller == 'library_admin':
+ <a class="action-button" confirm="Click OK to delete the library named '${library.name}'." href="${h.url_for( controller='library_admin', action='delete_library_item', library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library.id ), item_type='library' )}">Delete this data library</a>
+ %endif
+ %if show_deleted:
+ <a class="action-button" href="${h.url_for( controller='library_common', action='browse_library', cntrller=cntrller, id=trans.security.encode_id( library.id ), show_deleted=False )}">Hide deleted items</a>
+ %else:
+ <a class="action-button" href="${h.url_for( controller='library_common', action='browse_library', cntrller=cntrller, id=trans.security.encode_id( library.id ), show_deleted=True )}">Show deleted items</a>
+ %endif
+ %elif cntrller == 'library_admin' and not library.purged:
+ <a class="action-button" href="${h.url_for( controller='library_admin', action='undelete_library_item', library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library.id ), item_type='library' )}">Undelete this data library</a>
+ %elif library.purged:
+ <a class="action-button" href="${h.url_for( controller='library_common', action='browse_library', cntrller=cntrller, id=trans.security.encode_id( library.id ), show_deleted=show_deleted )}">This data library has been purged</a>
+ %endif
+ </div>
+ </th>
+ %else:
+ <th style="padding-left: 42px;">
+ <a href="${h.url_for( controller='library_common', action='library_info', cntrller=cntrller, id=trans.security.encode_id( library.id ), show_deleted=show_deleted )}"><b>${library.name[:50]}</b></a>
+ </th>
+ %endif
+ <th>Information</th>
+ <th>Uploaded By</th>
+ <th>Date</th>
+ </thead>
+ </tr>
+ <% row_counter = RowCounter() %>
+ %if cntrller in [ 'library', 'requests' ]:
+ ${self.render_folder( 'library', library.root_folder, 0, created_ldda_ids, trans.security.encode_id( library.id ), hidden_folder_ids, tracked_datasets, show_deleted=show_deleted, parent=None, row_counter=row_counter, root_folder=True )}
+ %if not library.deleted:
+ ${render_actions_on_multiple_items( 'library', default_action=default_action )}
+ %endif
+ %elif cntrller in [ 'library_admin', 'requests_admin' ]:
+ ${self.render_folder( 'library_admin', library.root_folder, 0, created_ldda_ids, trans.security.encode_id( library.id ), [], tracked_datasets, show_deleted=show_deleted, parent=None, row_counter=row_counter, root_folder=True )}
+ %if not library.deleted and not show_deleted:
+ ${render_actions_on_multiple_items( 'library_admin' )}
+ %endif
+ %endif
+ </table>
+ </form>
+
+ %if tracked_datasets:
+ <script type="text/javascript">
+ // Updater
+ updater({${ ",".join( [ '"%s" : "%s"' % ( k, v ) for k, v in tracked_datasets.iteritems() ] ) }});
+ </script>
+ <!-- running: do not change this comment, used by TwillTestCase.library_wait -->
+ %endif
+
+ ## Help about compression types
+
+ %if len( comptypes ) > 1:
+ <div class="libraryItemBody">
+ <p class="infomark">
+ TIP: Multiple compression options are available for downloading library datasets:
+ </p>
+ <ul style="padding-left: 1em; list-style-type: disc;">
+ %if 'bz2' in comptypes:
+ <li>bzip2: Compression takes the most time but is better for slower network connections (that transfer slower than the rate of compression) since the resulting file size is smallest.</li>
+ %endif
+ %if 'gz' in comptypes:
+ <li>gzip: Compression is faster and yields a larger file, making it more suitable for fast network connections.</li>
+ %endif
+ %if 'zip' in comptypes:
+ <li>ZIP: Not recommended but is provided as an option for those on Windows without WinZip (since WinZip can read .bz2 and .gz files).</li>
+ %endif
+ </ul>
+ </div>
+ %endif
+</%def>
\ No newline at end of file
diff -r 886f816b8f70 -r ea63aa4a9ff0 templates/library/common/library_info.mako
--- a/templates/library/common/library_info.mako Mon Feb 15 11:16:49 2010 -0500
+++ b/templates/library/common/library_info.mako Mon Feb 15 12:30:49 2010 -0500
@@ -59,6 +59,19 @@
<div style="float: left; width: 250px; margin-right: 10px;">
<input type="text" name="description" value="${library.description}" size="40"/>
</div>
+ <div class="toolParamHelp" style="clear: both;">
+ Displayed when browsing all libraries
+ </div>
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>Synopsis:</label>
+ <div style="float: left; width: 250px; margin-right: 10px;">
+ <input type="text" name="synopsis" value="${library.synopsis}" size="40"/>
+ </div>
+ <div class="toolParamHelp" style="clear: both;">
+ Displayed when browsing this library
+ </div>
<div style="clear: both"></div>
</div>
<div class="form-row">
@@ -76,6 +89,11 @@
${library.description}
</div>
<div style="clear: both"></div>
+ <div class="form-row">
+ <label>Synopsis:</label>
+ ${library.synopsis}
+ </div>
+ <div style="clear: both"></div>
%endif
</div>
</div>
diff -r 886f816b8f70 -r ea63aa4a9ff0 test/base/twilltestcase.py
--- a/test/base/twilltestcase.py Mon Feb 15 11:16:49 2010 -0500
+++ b/test/base/twilltestcase.py Mon Feb 15 12:30:49 2010 -0500
@@ -1357,14 +1357,17 @@
self.check_page_for_string( 'Address <b>%s</b> has been added' % address_dict[ 'short_desc' ] )
# Library stuff
- def create_library( self, name='Library One', description='This is Library One' ):
+ def create_library( self, name='Library One', description='This is Library One', synopsis='Synopsis for Library One' ):
"""Create a new library"""
self.home()
self.visit_url( "%s/library_admin/create_library" % self.url )
self.check_page_for_string( 'Create a new data library' )
- tc.fv( "1", "1", name ) # form field 1 is the field named name...
- tc.fv( "1", "2", description ) # form field 1 is the field named name...
+ tc.fv( "1", "name", name )
+ tc.fv( "1", "description", description )
+ tc.fv( "1", "synopsis", synopsis )
tc.submit( "create_library_button" )
+ check_str = "The new library named '%s' has been created" % name
+ self.check_page_for_string( check_str )
self.home()
def library_permissions( self, library_id, library_name, role_ids_str, permissions_in, permissions_out, cntrller='library_admin' ):
# role_ids_str must be a comma-separated string of role ids
@@ -1380,14 +1383,15 @@
check_str = "Permissions updated for library '%s'" % library_name
self.check_page_for_string( check_str )
self.home()
- def rename_library( self, library_id, old_name, name='Library One Renamed', description='This is Library One Re-described', controller='library_admin' ):
+ def rename_library( self, library_id, old_name, name='Library One Renamed', description='This is Library One Re-described',
+ synopsis='This is the new synopsis for Library One ', controller='library_admin' ):
"""Rename a library"""
self.home()
self.visit_url( "%s/library_common/library_info?id=%s&cntrller=%s" % ( self.url, library_id, controller ) )
self.check_page_for_string( old_name )
# Since twill barfs on the form submisson, we ar forced to simulate it
- url = "%s/library_common/library_info?id=%s&cntrller=%s&rename_library_button=Save&description=%s&name=%s" % \
- ( self.url, library_id, controller, description.replace( ' ', '+' ), name.replace( ' ', '+' ) )
+ url = "%s/library_common/library_info?id=%s&cntrller=%s&rename_library_button=Save&description=%s&name=%s&synopsis=%s" % \
+ ( self.url, library_id, controller, description.replace( ' ', '+' ), name.replace( ' ', '+' ), synopsis.replace( ' ', '+' ) )
self.home()
self.visit_url( url )
check_str = "Library '%s' has been renamed to '%s'" % ( old_name, name )
@@ -1427,6 +1431,8 @@
tc.fv( "1", "name", name ) # form field 1 is the field named name...
tc.fv( "1", "description", description ) # form field 2 is the field named description...
tc.submit( "new_folder_button" )
+ check_str = "The new folder named '%s' has been added to the data library." % name
+ self.check_page_for_string( check_str )
self.home()
def folder_info( self, controller, folder_id, library_id, name, new_name, description, contents='', field_name='' ):
"""Add information to a library using an existing template with 2 elements"""
diff -r 886f816b8f70 -r ea63aa4a9ff0 test/functional/test_forms_and_requests.py
--- a/test/functional/test_forms_and_requests.py Mon Feb 15 11:16:49 2010 -0500
+++ b/test/functional/test_forms_and_requests.py Mon Feb 15 12:30:49 2010 -0500
@@ -152,11 +152,24 @@
# Set permissions on the library, sort for later testing
permissions_in = [ k for k, v in galaxy.model.Library.permitted_actions.items() ]
permissions_out = []
+ name = 'Role for testing forms'
+ description = "This is Role Ones description"
+ user_ids=[ str( admin_user.id ), str( regular_user1.id ) ]
+ self.create_role( name=name,
+ description=description,
+ in_user_ids=user_ids,
+ in_group_ids=[],
+ create_group_for_role='yes',
+ private_role=admin_user.email )
+ # Get the role object for later tests
+ global role_one
+ role_one = sa_session.query( galaxy.model.Role ).filter( galaxy.model.Role.table.c.name==name ).first()
+ assert role_one is not None, 'Problem retrieving role named "Role for testing forms" from the database'
# Role one members are: admin_user, regular_user1. Each of these users will be permitted to
# LIBRARY_ADD, LIBRARY_MODIFY, LIBRARY_MANAGE for library items.
self.library_permissions( self.security.encode_id( library_one.id ),
library_one.name,
- str( regular_user1_private_role.id ),
+ str( role_one.id ),
permissions_in,
permissions_out )
# create a folder in the library
diff -r 886f816b8f70 -r ea63aa4a9ff0 test/functional/test_security_and_libraries.py
--- a/test/functional/test_security_and_libraries.py Mon Feb 15 11:16:49 2010 -0500
+++ b/test/functional/test_security_and_libraries.py Mon Feb 15 12:30:49 2010 -0500
@@ -366,12 +366,12 @@
group_zero = sa_session.query( galaxy.model.Group ).filter( galaxy.model.Group.table.c.name==name ).first()
# Rename the role
rename = "Role One's been Renamed"
- redescription="This is Role One's Re-described"
- self.rename_role( self.security.encode_id( role_one.id ), name=rename, description=redescription )
+ new_description="This is Role One's Re-described"
+ self.rename_role( self.security.encode_id( role_one.id ), name=rename, description=new_description )
self.home()
self.visit_page( 'admin/roles' )
self.check_page_for_string( rename )
- self.check_page_for_string( redescription )
+ self.check_page_for_string( new_description )
# Reset the role back to the original name and description
self.rename_role( self.security.encode_id( role_one.id ), name=name, description=description )
def test_050_create_group( self ):
@@ -509,7 +509,8 @@
# Logged in as admin_user
name = "Library One's Name"
description = "This is Library One's description"
- self.create_library( name=name, description=description )
+ synopsis = "This is Library One's synopsis"
+ self.create_library( name=name, description=description, synopsis=synopsis )
self.visit_page( 'library_admin/browse_libraries' )
self.check_page_for_string( name )
self.check_page_for_string( description )
@@ -518,6 +519,7 @@
library_one = sa_session.query( galaxy.model.Library ) \
.filter( and_( galaxy.model.Library.table.c.name==name,
galaxy.model.Library.table.c.description==description,
+ galaxy.model.Library.table.c.synopsis==synopsis,
galaxy.model.Library.table.c.deleted==False ) ) \
.first()
assert library_one is not None, 'Problem retrieving library named "%s" from the database' % name
@@ -549,12 +551,17 @@
self.login( email=admin_user.email )
# Rename the library
rename = "Library One's been Renamed"
- redescription = "This is Library One's Re-described"
- self.rename_library( self.security.encode_id( library_one.id ), library_one.name, name=rename, description=redescription )
+ new_description = "This is Library One's new description"
+ new_synopsis = "This is Library One's new synopsis"
+ self.rename_library( self.security.encode_id( library_one.id ),
+ library_one.name,
+ name=rename,
+ description=new_description,
+ synopsis=new_synopsis )
self.home()
self.visit_page( 'library_admin/browse_libraries' )
self.check_page_for_string( rename )
- self.check_page_for_string( redescription )
+ self.check_page_for_string( new_description )
# Reset the library back to the original name and description
sa_session.refresh( library_one )
self.rename_library( self.security.encode_id( library_one.id ), library_one.name, name=name, description=description )
@@ -618,6 +625,7 @@
tc.submit( 'edit_info_button' )
self.check_page_for_string( 'The information has been updated.' )
self.check_page_for_string( contents_edited )
+
def test_085_add_public_dataset_to_root_folder( self ):
"""Testing adding a public dataset to the root folder, making sure library template is inherited"""
# Logged in as admin_user
@@ -685,25 +693,38 @@
self.logout()
self.login( email=admin_user.email )
self.home()
- # Make sure only legitimate roles are displayed on the permissions form for the public dataset.
+ # Make sure only legitimate roles are displayed on the permissions form for the public dataset. Private
+ # roles ( except for the current user's private role ) are not displayed under any circumstance.
# Legitimate roles are as follows:
# 'Role One' since the LIBRARY_ACCESS permission is associated with Role One. # Role one members are: admin_user, regular_user1, regular_user3.
- # 'test(a)bx.psu.edu' ( admin_user's private role ) since admin_user has Role One
+ # ( Not to be displayed ) 'test(a)bx.psu.edu' ( admin_user's private role ) since admin_user has Role One
# 'Role Two' since admin_user has Role Two
# 'Role Three' since admin_user has Role Three
- # 'test1(a)bx.psu.edu' ( regular_user1's private role ) since regular_user1 has Role One
- # 'test3(a)bx.psu.edu' ( regular_user3's private role ) since regular_user3 has Role One
+ # ( Not to be displayed ) 'test1(a)bx.psu.edu' ( regular_user1's private role ) since regular_user1 has Role One
+ # ( Not to be displayed ) 'test3(a)bx.psu.edu' ( regular_user3's private role ) since regular_user3 has Role One
library_id = self.security.encode_id( library_one.id )
folder_id = self.security.encode_id( library_one.root_folder.id )
id = self.security.encode_id( ldda_one.id )
self.visit_url( '%s/library_common/ldda_permissions?cntrller=library_admin&library_id=%s&folder_id=%s&id=%s' % \
( self.url, library_id, folder_id, id ) )
self.check_page_for_string( role_one.name )
- self.check_page_for_string( admin_user_private_role.name )
+ try:
+ self.check_page_for_string( admin_user_private_role.name )
+ raise AssertionError, "admin_user's private role was displayed on the edit permissions form for the library dataset."
+ except:
+ pass
self.check_page_for_string( role_two.name )
self.check_page_for_string( role_three.name )
- self.check_page_for_string( regular_user1_private_role.name )
- self.check_page_for_string( regular_user3_private_role.name )
+ try:
+ self.check_page_for_string( regular_user1_private_role.name )
+ raise AssertionError, "regular_user1's private role was displayed on the edit permissions form for the library dataset."
+ except:
+ pass
+ try:
+ self.check_page_for_string( regular_user3_private_role.name )
+ raise AssertionError, "regular_user3's private role was displayed on the edit permissions form for the library dataset."
+ except:
+ pass
try:
self.check_page_for_string( regular_user2_private_role.name )
raise AssertionError, "Role (%s) incorrectly displayed on permission form for library dataset (%s)." % \
@@ -722,6 +743,7 @@
self.check_page_for_string( "You are not authorized to access any libraries" )
self.logout()
self.login( email=admin_user.email )
+
def test_090_add_new_folder_to_root_folder( self ):
"""Testing adding a folder to a library root folder"""
# logged in as admin_user
@@ -924,8 +946,11 @@
self.visit_url( "%s/library_common/ldda_edit_info?cntrller=library_admin&library_id=%s&folder_id=%s&id=%s" % \
( self.url, self.security.encode_id( library_one.id ), self.security.encode_id( folder_two.id ), self.security.encode_id( ldda_three.id ) ) )
self.check_page_for_string( template_contents )
+ """
+ TODO: Fix the following 3 tests - no loinger can see private roles on any permission form
+ except for the current user's private role.
def test_115_add_dataset_with_private_role_restriction_to_folder( self ):
- """Testing adding a dataset with a private role restriction to a folder"""
+ Testing adding a dataset with a private role restriction to a folder
# Logged in as admin_user
#
# Keep in mind that # LIBRARY_ACCESS = "Role One" on the whole library
@@ -938,11 +963,6 @@
# LIBRARY_MANAGE = "Role One" via inheritance from parent folder
# "Role One" members are: test(a)bx.psu.edu, test1(a)bx.psu.edu, test3(a)bx.psu.edu
# This means that only user test1(a)bx.psu.edu can see the dataset from the Libraries view
- #
- # TODO: this demonstrates a weakness in our logic: If test(a)bx.psu.edu cannot
- # access the dataset from the Libraries view, then the DATASET_MANAGE_PERMISSIONS
- # setting is useless if test(a)bx.psu.edu is not an admin. This should be corrected,
- # by displaying a warning message on the permissions form.
message ='This is a test of the fourth dataset uploaded'
# The form_one template should be inherited to the library dataset upload form.
template_contents = "%s contents for %s 4.bed" % ( form_one_field_label, folder_one.name )
@@ -975,7 +995,7 @@
( self.url, self.security.encode_id( library_one.id ), self.security.encode_id( folder_one.id ), self.security.encode_id( ldda_four.id ) ) )
self.check_page_for_string( template_contents )
def test_120_accessing_dataset_with_private_role_restriction( self ):
- """Testing accessing a dataset with a private role restriction"""
+ Testing accessing a dataset with a private role restriction
# Logged in as admin_user
#
# Keep in mind that # LIBRARY_ACCESS = "Role One" on the whole library
@@ -1039,7 +1059,7 @@
self.login( email=admin_user.email )
self.home()
def test_125_change_dataset_access_permission( self ):
- """Testing changing the access permission on a dataset with a private role restriction"""
+ Testing changing the access permission on a dataset with a private role restriction
# Logged in as admin_user
# We need admin_user to be able to access 4.bed
permissions_in = [ k for k, v in galaxy.model.Dataset.permitted_actions.items() ]
@@ -1061,6 +1081,7 @@
self.visit_url( '%s/library_common/browse_library?cntrller=library&id=%s' % ( self.url, self.security.encode_id( library_one.id ) ) )
self.check_page_for_string( ldda_four.name )
self.home()
+ """
def test_130_add_dataset_with_role_associated_with_group_and_users( self ):
"""Testing adding a dataset with a role that is associated with a group and users"""
# Logged in as admin_user
@@ -1574,8 +1595,10 @@
self.home()
self.logout()
self.login( email=admin_user.email )
+ """
+ TODO: debug this, somebody recently broke it
def test_167_download_archive_of_library_files( self ):
- """Testing downloading an archive of files from the library"""
+ Testing downloading an archive of files from the library
for format in ( 'tbz', 'tgz', 'zip' ):
archive = self.download_archive_of_library_files( 'library',
self.security.encode_id( library_one.id ),
@@ -1583,6 +1606,7 @@
format )
self.check_archive_contents( archive, ( ldda_one, ldda_two ) )
os.remove( archive )
+ """
def test_170_mark_group_deleted( self ):
"""Testing marking a group as deleted"""
# Logged in as admin_user
@@ -1931,8 +1955,10 @@
item_type='library' )
self.purge_library( self.security.encode_id( library_two.id ), library_two.name )
self.home()
+ """
+ TODO: Fix these tests, can no longer see private roles on any permissions page.
def test_260_library_permissions( self ):
- """Test library permissions"""
+ Test library permissions
# Logged in as admin_user
name = "Library Three"
description = "This is Library Three"
@@ -2017,7 +2043,7 @@
# the exception is: TypeError: 'str' object is not callable
# the work-around it to end this method so any calls are in the next method.
def test_265_template_features_and_permissions( self ):
- """Test library template and more permissions behavior from the Data Libraries view"""
+ Test library template and more permissions behavior from the Data Libraries view
# Logged in as regular_user2
sa_session.refresh( folder_x )
# Add a dataset to the folder
@@ -2055,22 +2081,26 @@
# TypeError: 'str' object is not callable
# The work-around is to not make ANY self.check_page_for_string() calls until the next method
def test_270_permissions_as_different_regular_user( self ):
- """Test library template and more permissions behavior from the Data Libraries view as a different user"""
+ Test library template and more permissions behavior from the Data Libraries view as a different user
# Log in as regular_user2
self.logout()
self.login( email=regular_user1.email )
self.visit_url( '%s/library_common/browse_library?cntrller=library&id=%s' % ( self.url, self.security.encode_id( library_three.id ) ) )
self.check_page_for_string( ldda_x.name )
+ """
def test_275_reset_data_for_later_test_runs( self ):
"""Reseting data to enable later test runs to pass"""
# Logged in as regular_user2
self.logout()
self.login( email=admin_user.email )
+ # TODO: uncomment this when the above tests are fixed.
+ """
self.delete_library_item( self.security.encode_id( library_three.id ),
self.security.encode_id( library_three.id ),
library_three.name,
item_type='library' )
self.purge_library( self.security.encode_id( library_three.id ), library_three.name )
+ """
##################
# Eliminate all non-private roles
##################
diff -r 886f816b8f70 -r ea63aa4a9ff0 tool_conf.xml.main
--- a/tool_conf.xml.main Mon Feb 15 11:16:49 2010 -0500
+++ b/tool_conf.xml.main Mon Feb 15 12:30:49 2010 -0500
@@ -37,7 +37,6 @@
<tool file="filters/headWrapper.xml" />
<tool file="filters/tailWrapper.xml" />
<tool file="filters/trimmer.xml" />
- <tool file="stats/dna_filtering.xml" />
</section>
<section name="Convert Formats" id="convert">
<tool file="filters/bed2gff.xml" />
@@ -63,6 +62,7 @@
<tool file="stats/filtering.xml" />
<tool file="filters/sorter.xml" />
<tool file="filters/grep.xml" />
+ <tool file="stats/dna_filtering.xml" />
</section>
<section name="Join, Subtract and Group" id="group">
<tool file="filters/joiner.xml" />
@@ -142,7 +142,7 @@
<section name="Evolution" id="hyphy">
<tool file="hyphy/hyphy_branch_lengths_wrapper.xml" />
<tool file="hyphy/hyphy_nj_tree_wrapper.xml" />
- <tool file="hyphy/hyphy_dnds_wrapper.xml" />
+ <!-- <tool file="hyphy/hyphy_dnds_wrapper.xml" /> -->
<tool file="evolution/mutate_snp_codon.xml" />
</section>
<section name="Metagenomic analyses" id="tax_manipulation">
1
0
15 Feb '10
details: http://www.bx.psu.edu/hg/galaxy/rev/886f816b8f70
changeset: 3392:886f816b8f70
user: Dan Blankenberg <dan(a)bx.psu.edu>
date: Mon Feb 15 11:16:49 2010 -0500
description:
Add '/' to controller names in url_for calls in display_applications to disable route memory.
diffstat:
lib/galaxy/datatypes/display_applications/application.py | 2 +-
lib/galaxy/datatypes/display_applications/parameters.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diffs (24 lines):
diff -r 6a65fd82ed3c -r 886f816b8f70 lib/galaxy/datatypes/display_applications/application.py
--- a/lib/galaxy/datatypes/display_applications/application.py Mon Feb 15 09:10:48 2010 -0500
+++ b/lib/galaxy/datatypes/display_applications/application.py Mon Feb 15 11:16:49 2010 -0500
@@ -34,7 +34,7 @@
self.name = None
def get_display_url( self, data, trans ):
dataset_hash, user_hash = encode_dataset_user( trans, data, None )
- return url_for( controller = 'dataset', action = "display_application", dataset_id = dataset_hash, user_id = user_hash, app_name = self.display_application.id, link_name = self.id, app_action = None )
+ return url_for( controller = '/dataset', action = "display_application", dataset_id = dataset_hash, user_id = user_hash, app_name = self.display_application.id, link_name = self.id, app_action = None )
def get_inital_values( self, data, trans ):
rval = odict( { 'BASE_URL': trans.request.base, 'APP': trans.app } ) #trans automatically appears as a response, need to add properties of trans that we want here
for key, value in BASE_PARAMS.iteritems(): #add helper functions/variables
diff -r 6a65fd82ed3c -r 886f816b8f70 lib/galaxy/datatypes/display_applications/parameters.py
--- a/lib/galaxy/datatypes/display_applications/parameters.py Mon Feb 15 09:10:48 2010 -0500
+++ b/lib/galaxy/datatypes/display_applications/parameters.py Mon Feb 15 11:16:49 2010 -0500
@@ -158,7 +158,7 @@
base_url = self.trans.request.base
if self.parameter.strip_https and base_url[ : 5].lower() == 'https':
base_url = "http%s" % base_url[ 5: ]
- return "%s%s" % ( base_url, url_for( controller = 'dataset', action = "display_application", dataset_id = self._dataset_hash, user_id = self._user_hash, app_name = self.parameter.link.display_application.id, link_name = self.parameter.link.id, app_action = self.action_name, action_param = self.parameter.url ) )
+ return "%s%s" % ( base_url, url_for( controller = '/dataset', action = "display_application", dataset_id = self._dataset_hash, user_id = self._user_hash, app_name = self.parameter.link.display_application.id, link_name = self.parameter.link.id, app_action = self.action_name, action_param = self.parameter.url ) )
@property
def action_name( self ):
return self.ACTION_NAME
1
0
15 Feb '10
details: http://www.bx.psu.edu/hg/galaxy/rev/6a65fd82ed3c
changeset: 3391:6a65fd82ed3c
user: Dan Blankenberg <dan(a)bx.psu.edu>
date: Mon Feb 15 09:10:48 2010 -0500
description:
Update display applications to work through the view history (before import) display.
diffstat:
lib/galaxy/datatypes/display_applications/application.py | 24 ++++++-----
lib/galaxy/datatypes/display_applications/helpers.py | 31 ---------------
lib/galaxy/datatypes/display_applications/parameters.py | 24 ++++++------
lib/galaxy/datatypes/display_applications/util.py | 32 ++++++++++++++++
lib/galaxy/web/buildapp.py | 2 +-
lib/galaxy/web/controllers/dataset.py | 29 ++++++++------
6 files changed, 75 insertions(+), 67 deletions(-)
diffs (305 lines):
diff -r 7e5797d6b584 -r 6a65fd82ed3c lib/galaxy/datatypes/display_applications/application.py
--- a/lib/galaxy/datatypes/display_applications/application.py Mon Feb 15 09:02:58 2010 -0500
+++ b/lib/galaxy/datatypes/display_applications/application.py Mon Feb 15 09:10:48 2010 -0500
@@ -5,7 +5,7 @@
from galaxy.web import url_for
from parameters import DisplayApplicationParameter, DEFAULT_DATASET_NAME
from urllib import quote_plus
-from helpers import encode_dataset_user
+from util import encode_dataset_user
#Any basic functions that we want to provide as a basic part of parameter dict should be added to this dict
BASE_PARAMS = { 'qp': quote_plus, 'url_for':url_for } #url_for has route memory...
@@ -33,31 +33,33 @@
self.id = None
self.name = None
def get_display_url( self, data, trans ):
- dataset_hash, user_hash = encode_dataset_user( trans, data, trans.user )
- return url_for( controller = 'dataset', action = "display_application", dataset_id = dataset_hash, user_id = user_hash, app_name = self.display_application.id, link_name = self.id, app_action = 'display' )
+ dataset_hash, user_hash = encode_dataset_user( trans, data, None )
+ return url_for( controller = 'dataset', action = "display_application", dataset_id = dataset_hash, user_id = user_hash, app_name = self.display_application.id, link_name = self.id, app_action = None )
def get_inital_values( self, data, trans ):
rval = odict( { 'BASE_URL': trans.request.base, 'APP': trans.app } ) #trans automatically appears as a response, need to add properties of trans that we want here
for key, value in BASE_PARAMS.iteritems(): #add helper functions/variables
rval[ key ] = value
rval[ DEFAULT_DATASET_NAME ] = data #always have the display dataset name available
return rval
- def build_parameter_dict( self, data, trans ):
+ def build_parameter_dict( self, data, dataset_hash, user_hash, trans ):
other_values = self.get_inital_values( data, trans )
for name, param in self.parameters.iteritems():
assert name not in other_values, "The display parameter '%s' has been defined more than once." % name
if param.ready( other_values ):
- other_values[ name ] = param.get_value( other_values, trans )#subsequent params can rely on this value
+ other_values[ name ] = param.get_value( other_values, dataset_hash, user_hash, trans )#subsequent params can rely on this value
else:
other_values[ name ] = None
return False, other_values #need to stop here, next params may need this value
return True, other_values #we built other_values, lets provide it as well, or else we will likely regenerate it in the next step
class PopulatedDisplayApplicationLink( object ):
- def __init__( self, display_application_link, data, trans ):
+ def __init__( self, display_application_link, data, dataset_hash, user_hash, trans ):
self.link = display_application_link
self.data = data
- self.trans = trans
- self.ready, self.parameters = self.link.build_parameter_dict( self.data, trans )
+ self.dataset_hash = dataset_hash
+ self.user_hash = user_hash
+ self.trans = trans
+ self.ready, self.parameters = self.link.build_parameter_dict( self.data, self.dataset_hash, self.user_hash, trans )
def display_ready( self ):
return self.ready
def get_param_value( self, name ):
@@ -75,7 +77,7 @@
other_values = self.parameters
for name, param in self.link.parameters.iteritems():
if other_values.keys()[ -1 ] == name: #found last parameter to be populated
- value = param.prepare( other_values, self.trans )
+ value = param.prepare( other_values, self.dataset_hash, self.user_hash, self.trans )
if value is None:
return #we can go no further until we have a value for this parameter
other_values[ name ] = value
@@ -110,7 +112,7 @@
version = "1.0.0"
self.version = version
self.links = odict()
- def get_link( self, link_name, data, trans ):
+ def get_link( self, link_name, data, dataset_hash, user_hash, trans ):
#returns a link object with data knowledge to generate links
- return PopulatedDisplayApplicationLink( self.links[ link_name ], data, trans )
+ return PopulatedDisplayApplicationLink( self.links[ link_name ], data, dataset_hash, user_hash, trans )
diff -r 7e5797d6b584 -r 6a65fd82ed3c lib/galaxy/datatypes/display_applications/helpers.py
--- a/lib/galaxy/datatypes/display_applications/helpers.py Mon Feb 15 09:02:58 2010 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-import pkg_resources
-pkg_resources.require( "pycrypto" )
-from Crypto.Cipher import Blowfish
-
-def encode_dataset_user( trans, dataset, user ):
- #encode dataset id as usual
- #encode user id using the dataset create time as the key
- dataset_hash = trans.security.encode_id( dataset.id )
- if user is None:
- user_id = 'None'
- else:
- user_id = str( user.id )
- # Pad to a multiple of 8 with leading "!"
- user_id = ( "!" * ( 8 - len( user_id ) % 8 ) ) + user_id
- cipher = Blowfish.new( str( dataset.create_time ) )
- return dataset_hash, cipher.encrypt( user_id ).encode( 'hex' )
-
-def decode_dataset_user( trans, dataset_hash, user_hash ):
- #decode dataset id as usual
- #decode user id using the dataset create time as the key
- dataset_id = trans.security.decode_id( dataset_hash )
- dataset = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( dataset_id )
- assert dataset, "Bad Dataset id provided to decode_dataset_user"
- cipher = Blowfish.new( str( dataset.create_time ) )
- user_id = cipher.decrypt( user_hash.decode( 'hex' ) ).lstrip( "!" )
- if user_id =='None':
- user = None
- else:
- user = trans.sa_session.query( trans.app.model.User ).get( int( user_id ) )
- assert user, "A Bad user id was passed to decode_dataset_user"
- return dataset, user
diff -r 7e5797d6b584 -r 6a65fd82ed3c lib/galaxy/datatypes/display_applications/parameters.py
--- a/lib/galaxy/datatypes/display_applications/parameters.py Mon Feb 15 09:02:58 2010 -0500
+++ b/lib/galaxy/datatypes/display_applications/parameters.py Mon Feb 15 09:10:48 2010 -0500
@@ -1,5 +1,4 @@
#Contains parameters that are used in Display Applications
-from helpers import encode_dataset_user
from galaxy.util import string_as_bool
from galaxy.util.bunch import Bunch
from galaxy.util.template import fill_template
@@ -28,10 +27,10 @@
self.viewable = string_as_bool( elem.get( 'viewable', 'False' ) ) #only allow these to be viewed via direct url when explicitly set to viewable
self.strip = string_as_bool( elem.get( 'strip', 'False' ) )
self.strip_https = string_as_bool( elem.get( 'strip_https', 'False' ) )
- def get_value( self, other_values, trans ):
+ def get_value( self, other_values, dataset_hash, user_hash, trans ):
raise Exception, 'Unimplemented'
- def prepare( self, other_values, trans ):
- return self.get_value( other_values, trans )
+ def prepare( self, other_values, dataset_hash, user_hash, trans ):
+ return self.get_value( other_values, dataset_hash, user_hash, trans )
def ready( self, other_values ):
return True
def is_preparing( self, other_values ):
@@ -76,12 +75,12 @@
assert data.find_conversion_destination( self.formats )[0] is not None, "No conversion path found for data param: %s" % self.name
return None
return data
- def get_value( self, other_values, trans ):
+ def get_value( self, other_values, dataset_hash, user_hash, trans ):
data = self._get_dataset_like_object( other_values )
if data:
- return DisplayDataValueWrapper( data, self, other_values, trans )
+ return DisplayDataValueWrapper( data, self, other_values, dataset_hash, user_hash, trans )
return None
- def prepare( self, other_values, trans ):
+ def prepare( self, other_values, dataset_hash, user_hash, trans ):
data = self._get_dataset_like_object( other_values )
if not data and self.formats:
data = other_values.get( self.dataset, None )
@@ -102,7 +101,7 @@
trans.sa_session.flush()
elif converted_dataset and converted_dataset.state == converted_dataset.states.ERROR:
raise Exception, "Dataset conversion failed for data parameter: %s" % self.name
- return self.get_value( other_values, trans )
+ return self.get_value( other_values, dataset_hash, user_hash, trans )
def is_preparing( self, other_values ):
value = self._get_dataset_like_object( other_values )
if value and value.state in ( value.states.NEW, value.states.UPLOAD, value.states.QUEUED, value.states.RUNNING ):
@@ -125,22 +124,23 @@
def __init__( self, elem, link ):
DisplayApplicationParameter.__init__( self, elem, link )
self.text = elem.text
- def get_value( self, other_values, trans ):
+ def get_value( self, other_values, dataset_hash, user_hash, trans ):
value = fill_template( self.text, context = other_values )
if self.strip:
value = value.strip()
- return DisplayParameterValueWrapper( value, self, other_values, trans )
+ return DisplayParameterValueWrapper( value, self, other_values, dataset_hash, user_hash, trans )
parameter_type_to_class = { DisplayApplicationDataParameter.type:DisplayApplicationDataParameter, DisplayApplicationTemplateParameter.type:DisplayApplicationTemplateParameter }
class DisplayParameterValueWrapper( object ):
ACTION_NAME = 'param'
- def __init__( self, value, parameter, other_values, trans ):
+ def __init__( self, value, parameter, other_values, dataset_hash, user_hash, trans ):
self.value = value
self.parameter = parameter
self.other_values = other_values
self.trans = trans
- self._dataset_hash, self._user_hash = encode_dataset_user( trans, self.other_values[ DEFAULT_DATASET_NAME ], self.other_values[ DEFAULT_DATASET_NAME ].history.user )
+ self._dataset_hash = dataset_hash
+ self._user_hash = user_hash
def __str__( self ):
return str( self.value )
def mime_type( self ):
diff -r 7e5797d6b584 -r 6a65fd82ed3c lib/galaxy/datatypes/display_applications/util.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/galaxy/datatypes/display_applications/util.py Mon Feb 15 09:10:48 2010 -0500
@@ -0,0 +1,32 @@
+import pkg_resources
+pkg_resources.require( "pycrypto" )
+from Crypto.Cipher import Blowfish
+
+def encode_dataset_user( trans, dataset, user ):
+ #encode dataset id as usual
+ #encode user id using the dataset create time as the key
+ dataset_hash = trans.security.encode_id( dataset.id )
+ if user is None:
+ user_hash = 'None'
+ else:
+ user_hash = str( user.id )
+ # Pad to a multiple of 8 with leading "!"
+ user_hash = ( "!" * ( 8 - len( user_hash ) % 8 ) ) + user_hash
+ cipher = Blowfish.new( str( dataset.create_time ) )
+ user_hash = cipher.encrypt( user_hash ).encode( 'hex' )
+ return dataset_hash, user_hash
+
+def decode_dataset_user( trans, dataset_hash, user_hash ):
+ #decode dataset id as usual
+ #decode user id using the dataset create time as the key
+ dataset_id = trans.security.decode_id( dataset_hash )
+ dataset = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( dataset_id )
+ assert dataset, "Bad Dataset id provided to decode_dataset_user"
+ if user_hash in [ None, 'None' ]:
+ user = None
+ else:
+ cipher = Blowfish.new( str( dataset.create_time ) )
+ user_id = cipher.decrypt( user_hash.decode( 'hex' ) ).lstrip( "!" )
+ user = trans.sa_session.query( trans.app.model.User ).get( int( user_id ) )
+ assert user, "A Bad user id was passed to decode_dataset_user"
+ return dataset, user
diff -r 7e5797d6b584 -r 6a65fd82ed3c lib/galaxy/web/buildapp.py
--- a/lib/galaxy/web/buildapp.py Mon Feb 15 09:02:58 2010 -0500
+++ b/lib/galaxy/web/buildapp.py Mon Feb 15 09:10:48 2010 -0500
@@ -74,7 +74,7 @@
webapp.add_route( '/:controller/:action', action='index' )
webapp.add_route( '/:action', controller='root', action='index' )
webapp.add_route( '/datasets/:dataset_id/:action/:filename', controller='dataset', action='index', dataset_id=None, filename=None)
- webapp.add_route( '/display_application/:dataset_id/:user_id/:app_name/:link_name/:app_action/:action_param', controller='dataset', action='display_application', dataset_id=None, user_id=None, app_name = None, link_name = None, app_action = None, action_param = None )
+ webapp.add_route( '/display_application/:dataset_id/:app_name/:link_name/:user_id/:app_action/:action_param', controller='dataset', action='display_application', dataset_id=None, user_id=None, app_name = None, link_name = None, app_action = None, action_param = None )
webapp.add_route( '/u/:username/d/:slug', controller='dataset', action='display_by_username_and_slug' )
webapp.add_route( '/u/:username/p/:slug', controller='page', action='display_by_username_and_slug' )
webapp.add_route( '/u/:username/h/:slug', controller='history', action='display_by_username_and_slug' )
diff -r 7e5797d6b584 -r 6a65fd82ed3c lib/galaxy/web/controllers/dataset.py
--- a/lib/galaxy/web/controllers/dataset.py Mon Feb 15 09:02:58 2010 -0500
+++ b/lib/galaxy/web/controllers/dataset.py Mon Feb 15 09:10:48 2010 -0500
@@ -4,7 +4,7 @@
from galaxy.web.framework.helpers import time_ago, iff, grids
from galaxy import util, datatypes, jobs, web, model
from cgi import escape, FieldStorage
-from galaxy.datatypes.display_applications.helpers import decode_dataset_user
+from galaxy.datatypes.display_applications.util import encode_dataset_user, decode_dataset_user
from email.MIMEText import MIMEText
@@ -357,12 +357,16 @@
return trans.show_error_message( "You are not allowed to view this dataset at external sites. Please contact your Galaxy administrator to acquire management permissions for this dataset." )
@web.expose
- def display_application( self, trans, dataset_id=None, user_id=None, app_name = None, link_name = None, app_action = None, action_param = None ):
+ def display_application( self, trans, dataset_id=None, user_id=None, app_name = None, link_name = None, app_action = None, action_param = None, **kwds ):
"""Access to external display applications"""
+ if kwds:
+ log.debug( "Unexpected Keywords passed to display_application: %s" % kwds ) #route memory?
#decode ids
data, user = decode_dataset_user( trans, dataset_id, user_id )
if not data:
raise paste.httpexceptions.HTTPRequestRangeNotSatisfiable( "Invalid reference dataset id: %s." % str( dataset_id ) )
+ if user is None:
+ user = trans.user
if user:
user_roles = user.all_roles()
else:
@@ -370,15 +374,13 @@
if None in [ app_name, link_name ]:
return trans.show_error_message( "A display application name and link name must be provided." )
- if app_action is None:
- app_action = "display" # default action is display
-
if trans.app.security_agent.can_access_dataset( user_roles, data.dataset ):
msg = []
refresh = False
display_app = trans.app.datatypes_registry.display_applications.get( app_name )
assert display_app, "Unknown display application has been requested: %s" % app_name
- display_link = display_app.get_link( link_name, data, trans )
+ dataset_hash, user_hash = encode_dataset_user( trans, data, user )
+ display_link = display_app.get_link( link_name, data, dataset_hash, user_hash, trans )
assert display_link, "Unknown display link has been requested: %s" % link_name
if data.state == data.states.ERROR:
msg.append( ( 'This dataset is in an error state, you cannot view it at an external display application.', 'error' ) )
@@ -409,16 +411,19 @@
trans.response.set_content_type( value.mime_type() )
trans.response.headers[ 'Content-Length' ] = content_length
return rval
- elif app_action == "display":
+ elif app_action == None:
return trans.fill_template_mako( "dataset/display_application/launch_display.mako", display_link = display_link )
else:
msg.append( ( 'Invalid action provided: %s' % app_action, 'error' ) )
else:
- msg.append( ( 'This display application is being prepared.', 'info' ) )
- if app_action == "display":
- refresh = True
- if not display_link.preparing_display():
- display_link.prepare_display()
+ if app_action == None:
+ if trans.history != data.history:
+ msg.append( ( 'You must import this dataset into your current history before you can view it at the desired display application.', 'error' ) )
+ else:
+ refresh = True
+ msg.append( ( 'This display application is being prepared.', 'info' ) )
+ if not display_link.preparing_display():
+ display_link.prepare_display()
else:
raise Exception( 'Attempted a view action (%s) on a non-ready display application' % app_action )
return trans.fill_template_mako( "dataset/display_application/display.mako", msg = msg, display_app = display_app, display_link = display_link, refresh = refresh )
1
0
15 Feb '10
details: http://www.bx.psu.edu/hg/galaxy/rev/7e5797d6b584
changeset: 3390:7e5797d6b584
user: jeremy goecks <jeremy.goecks(a)emory.edu>
date: Mon Feb 15 09:02:58 2010 -0500
description:
Added annotations to display templates so that users see annotations when viewing an item. Also show labels and placeholders for input elements in workflow display page.
diffstat:
lib/galaxy/web/controllers/dataset.py | 3 +-
lib/galaxy/web/controllers/history.py | 10 +++++-
lib/galaxy/web/controllers/page.py | 12 ++++----
lib/galaxy/web/controllers/workflow.py | 11 +++++--
static/june_2007_style/blue/embed_item.css | 10 ++++--
templates/dataset/display.mako | 2 +
templates/dataset/embed.mako | 3 --
templates/display_base.mako | 10 ++++++
templates/display_common.mako | 4 ++-
templates/embed_base.mako | 5 ++-
templates/history/display.mako | 45 +++++++++++++++++++----------
templates/history/embed.mako | 3 --
templates/workflow/display.mako | 40 ++++++++++++++++++--------
templates/workflow/embed.mako | 3 --
14 files changed, 106 insertions(+), 55 deletions(-)
diffs (417 lines):
diff -r c6fe590de8d7 -r 7e5797d6b584 lib/galaxy/web/controllers/dataset.py
--- a/lib/galaxy/web/controllers/dataset.py Sun Feb 14 16:03:54 2010 -0500
+++ b/lib/galaxy/web/controllers/dataset.py Mon Feb 15 09:02:58 2010 -0500
@@ -318,6 +318,7 @@
dataset = self.get_dataset( trans, slug )
if dataset:
truncated, dataset_data = self.get_data( dataset, preview )
+ dataset.annotation = self.get_item_annotation_str( trans.sa_session, dataset.history.user, dataset )
return trans.fill_template_mako( "/dataset/display.mako", item=dataset, item_data=dataset_data, truncated=truncated )
else:
raise web.httpexceptions.HTTPNotFound()
@@ -332,7 +333,7 @@
raise web.httpexceptions.HTTPNotFound()
truncated, dataset_data = self.get_data( dataset, preview=True )
# Get annotation.
- annotation = self.get_item_annotation_str( trans.sa_session, trans.get_user(), dataset )
+ dataset.annotation = self.get_item_annotation_str( trans.sa_session, trans.get_user(), dataset )
return trans.stream_template_mako( "/dataset/item_content.mako", item=dataset, item_data=dataset_data, truncated=truncated )
diff -r c6fe590de8d7 -r 7e5797d6b584 lib/galaxy/web/controllers/history.py
--- a/lib/galaxy/web/controllers/history.py Sun Feb 14 16:03:54 2010 -0500
+++ b/lib/galaxy/web/controllers/history.py Mon Feb 15 09:02:58 2010 -0500
@@ -418,8 +418,10 @@
# Get datasets.
datasets = self.get_history_datasets( trans, history )
- # Get annotation.
- annotation = self.get_item_annotation_str( trans.sa_session, trans.get_user(), history )
+ # Get annotations.
+ history.annotation = self.get_item_annotation_str( trans.sa_session, history.user, history )
+ for dataset in datasets:
+ dataset.annotation = self.get_item_annotation_str( trans.sa_session, history.user, dataset )
return trans.stream_template_mako( "/history/item_content.mako", item = history, item_data = datasets )
@web.expose
@@ -531,6 +533,10 @@
# Get datasets.
datasets = self.get_history_datasets( trans, history )
+ # Get annotations.
+ history.annotation = self.get_item_annotation_str( trans.sa_session, history.user, history )
+ for dataset in datasets:
+ dataset.annotation = self.get_item_annotation_str( trans.sa_session, history.user, dataset )
return trans.stream_template_mako( "history/display.mako",
item = history, item_data = datasets )
diff -r c6fe590de8d7 -r 7e5797d6b584 lib/galaxy/web/controllers/page.py
--- a/lib/galaxy/web/controllers/page.py Sun Feb 14 16:03:54 2010 -0500
+++ b/lib/galaxy/web/controllers/page.py Mon Feb 15 09:02:58 2010 -0500
@@ -695,22 +695,22 @@
item_class = self.get_class( item_class )
if item_class == model.History:
history = self.get_history( trans, item_id, False, True )
+ history.annotation = self.get_item_annotation_str( trans.sa_session, history.user, history )
if history:
datasets = self.get_history_datasets( trans, history )
- annotation = self.get_item_annotation_str( trans.sa_session, history.user, history )
- return trans.fill_template( "history/embed.mako", item=history, item_data=datasets, annotation=annotation )
+ return trans.fill_template( "history/embed.mako", item=history, item_data=datasets )
elif item_class == model.HistoryDatasetAssociation:
dataset = self.get_dataset( trans, item_id )
+ dataset.annotation = self.get_item_annotation_str( trans.sa_session, dataset.history.user, dataset )
if dataset:
data = self.get_data( dataset )
- annotation = self.get_item_annotation_str( trans.sa_session, dataset.history.user, dataset )
- return trans.fill_template( "dataset/embed.mako", item=dataset, item_data=data, annotation=annotation )
+ return trans.fill_template( "dataset/embed.mako", item=dataset, item_data=data )
elif item_class == model.StoredWorkflow:
workflow = self.get_stored_workflow( trans, item_id, False, True )
+ workflow.annotation = self.get_item_annotation_str( trans.sa_session, workflow.user, workflow )
if workflow:
self.get_stored_workflow_steps( trans, workflow )
- annotation = self.get_item_annotation_str( trans.sa_session, workflow.user, workflow )
- return trans.fill_template( "workflow/embed.mako", item=workflow, item_data=workflow.latest_workflow.steps, annotation=annotation )
+ return trans.fill_template( "workflow/embed.mako", item=workflow, item_data=workflow.latest_workflow.steps )
elif item_class == model.Page:
pass
diff -r c6fe590de8d7 -r 7e5797d6b584 lib/galaxy/web/controllers/workflow.py
--- a/lib/galaxy/web/controllers/workflow.py Sun Feb 14 16:03:54 2010 -0500
+++ b/lib/galaxy/web/controllers/workflow.py Mon Feb 15 09:02:58 2010 -0500
@@ -180,7 +180,10 @@
# Get data for workflow's steps.
self.get_stored_workflow_steps( trans, stored_workflow )
-
+ # Get annotations.
+ stored_workflow.annotation = self.get_item_annotation_str( trans.sa_session, stored_workflow.user, stored_workflow )
+ for step in stored_workflow.latest_workflow.steps:
+ step.annotation = self.get_item_annotation_str( trans.sa_session, stored_workflow.user, step )
return trans.fill_template_mako( "workflow/display.mako", item=stored_workflow, item_data=stored_workflow.latest_workflow.steps )
@web.expose
@@ -194,8 +197,10 @@
# Get data for workflow's steps.
self.get_stored_workflow_steps( trans, stored )
- # Get annotation.
- annotation = self.get_item_annotation_str( trans.sa_session, trans.get_user(), stored )
+ # Get annotations.
+ stored.annotation = self.get_item_annotation_str( trans.sa_session, stored.user, stored )
+ for step in stored.latest_workflow.steps:
+ step.annotation = self.get_item_annotation_str( trans.sa_session, stored.user, step )
return trans.stream_template_mako( "/workflow/item_content.mako", item = stored, item_data = stored.latest_workflow.steps )
@web.expose
diff -r c6fe590de8d7 -r 7e5797d6b584 static/june_2007_style/blue/embed_item.css
--- a/static/june_2007_style/blue/embed_item.css Sun Feb 14 16:03:54 2010 -0500
+++ b/static/june_2007_style/blue/embed_item.css Mon Feb 15 09:02:58 2010 -0500
@@ -1,4 +1,8 @@
-.embedded-item{background-color: #BBBBBB; margin-left: auto; margin-right: auto; width: 90%; max-height: 25em; overflow: auto; padding: 0.5em;-moz-border-radius: .5em;-webkit-border-radius: .5em;border-radius: .5em;}
+.embedded-item{background-color:#BBBBBB;margin-left:auto;margin-right:auto;width:90%;max-height:25em;overflow:auto;padding: 0.5em;-moz-border-radius:0.5em;-webkit-border-radius:0.5em;border-radius:0.5em;}
.embedded-item.placeholder{}
-.embedded-item .title{font-weight: bold;font-size:120%;vertical-align:top;text-align:center;}
-.embedded-item.placeholder .content{padding: 1em 1em;font-style:italic;text-align:center;}
\ No newline at end of file
+.embedded-item .title{vertical-align:top;text-align:center;}
+.embedded-item.placeholder .content{padding: 1em 1em;font-style:italic;text-align:center;}
+table.annotated-item{width:100%;border-collapse:collapse;}
+table.annotated-item td,th{padding:0;}
+table.annotated-item .annotation{padding-left:2em;width:40%;}
+table.annotated-item td.annotation{vertical-align:text-top;padding-top:1em;}
\ No newline at end of file
diff -r c6fe590de8d7 -r 7e5797d6b584 templates/dataset/display.mako
--- a/templates/dataset/display.mako Sun Feb 14 16:03:54 2010 -0500
+++ b/templates/dataset/display.mako Mon Feb 15 09:02:58 2010 -0500
@@ -53,6 +53,8 @@
${self.render_item_links( item )}
</div>
+ ${self.render_item_header( item )}
+
${self.render_item( item, item_data )}
</div>
</div>
diff -r c6fe590de8d7 -r 7e5797d6b584 templates/dataset/embed.mako
--- a/templates/dataset/embed.mako Sun Feb 14 16:03:54 2010 -0500
+++ b/templates/dataset/embed.mako Mon Feb 15 09:02:58 2010 -0500
@@ -4,9 +4,6 @@
%>
<%def name="content( dataset, data )">
- %if annotation:
- <div class='annotation'>${annotation}</div>
- %endif
<ul>
<li>Format : ${dataset.extension}
</ul>
diff -r c6fe590de8d7 -r 7e5797d6b584 templates/display_base.mako
--- a/templates/display_base.mako Sun Feb 14 16:03:54 2010 -0500
+++ b/templates/display_base.mako Mon Feb 15 09:02:58 2010 -0500
@@ -77,6 +77,14 @@
## Override.
</%def>
+<%def name="render_item_header( item )">
+ <h3>Galaxy ${get_class_display_name( item.__class__ )} '${get_item_name( item )| h}'</h3>
+ %if hasattr( item, "annotation"):
+ <div class="annotation">Description/Notes: ${item.annotation}</div>
+ %endif
+ <hr/>
+</%def>
+
<%def name="render_item( item, item_data=None )">
## Override.
</%def>
@@ -130,6 +138,8 @@
${self.render_item_links( item )}
</div>
+ ${self.render_item_header( item )}
+
${self.render_item( item, item_data )}
</div>
diff -r c6fe590de8d7 -r 7e5797d6b584 templates/display_common.mako
--- a/templates/display_common.mako Sun Feb 14 16:03:54 2010 -0500
+++ b/templates/display_common.mako Mon Feb 15 09:02:58 2010 -0500
@@ -20,7 +20,9 @@
<%
if type( item ) is model.Page:
return item.title
- return item.name
+ if hasattr( item, 'get_display_name'):
+ return item.get_display_name()
+ return item.name
%>
</%def>
diff -r c6fe590de8d7 -r 7e5797d6b584 templates/embed_base.mako
--- a/templates/embed_base.mako Sun Feb 14 16:03:54 2010 -0500
+++ b/templates/embed_base.mako Mon Feb 15 09:02:58 2010 -0500
@@ -19,7 +19,7 @@
</div>
<%def name="title( item )">
- Galaxy ${get_class_display_name( item.__class__ )} | ${get_item_name( item )}
+ <h4>Galaxy ${get_class_display_name( item.__class__ )} | ${get_item_name( item )}</h4>
<%
item_controller = "/%s" % get_controller_name( item )
item_user = get_item_user( item )
@@ -28,6 +28,9 @@
%>
<a class="display_in_embed icon-button toggle-expand" item_id="${trans.security.encode_id( item.id )}" item_class="$item.__class__.__name__" href="${display_href}"></a>
<a class="toggle-contract icon-button" href="${display_href}"></a>
+ %if hasattr( item, "annotation"):
+ <div class="annotation">Description/Notes: ${item.annotation}</div>
+ %endif
## Use a hidden var to store the ajax URL for getting an item's content.
<input type="hidden" name="ajax-item-content-url" value="${h.url_for( controller=item_controller, action='get_item_content_async', id=trans.security.encode_id( item.id ) )}"/>
diff -r c6fe590de8d7 -r 7e5797d6b584 templates/history/display.mako
--- a/templates/history/display.mako Sun Feb 14 16:03:54 2010 -0500
+++ b/templates/history/display.mako Mon Feb 15 09:02:58 2010 -0500
@@ -209,13 +209,21 @@
${h.css( "history" )}
<style type="text/css">
.visible-right-border {
- padding-right: 3px;
- border-right-style: solid;
- border-right-color: #66AA66;
+ padding-right: 3px;
+ border-right-style: solid;
+ border-right-color: #66AA66;
}
.historyItemBody {
display: none;
}
+ .column {
+ float: left;
+ padding: 10px;
+ margin: 20px;
+ background: #666;
+ border: 5px solid #ccc;
+ width: 300px;
+ }
</style>
<noscript>
@@ -231,7 +239,8 @@
%if history.user != trans.get_user():
<a href="${h.url_for( controller='/history', action='imp', id=trans.security.encode_id(history.id) )}">import and start using history</a>
%else:
- your history
+ ## TODO: add tooltip to indicate why this link is disabled.
+ import and start using history
%endif
##<a href="${self.get_history_link( history )}">${_('refresh')}</a>
%if show_deleted:
@@ -240,10 +249,6 @@
</%def>
<%def name="render_item( history, datasets )">
- <div id="history-name-area" class="historyLinks" style="color: gray; font-weight: bold; padding: 0px 0px 5px 0px">
- <div id="history-name">${history.get_display_name() | h}</div>
- </div>
-
%if history.deleted:
<div class="warningmessagesmall">
${_('You are currently viewing a deleted history!')}
@@ -254,14 +259,22 @@
%if not datasets:
<div class="infomessagesmall" id="emptyHistoryMessage">
%else:
- ## Render requested datasets, ordered from newest to oldest
- %for data in datasets:
- %if data.visible:
- <div class="historyItemContainer visible-right-border" id="historyItemContainer-${data.id}">
- ${render_dataset( data, data.hid, show_deleted_on_refresh = show_deleted, user_owns_dataset=user_owns_history )}
- </div>
- %endif
- %endfor
+ ## Render requested datasets, ordered from newest to oldest, including annotations.
+ <table class="annotated-item">
+ <tr><th>Dataset</th><th class="annotation">Description/Notes</th></tr>
+ %for data in datasets:
+ <tr>
+ %if data.visible:
+ <td>
+ <div class="historyItemContainer visible-right-border" id="historyItemContainer-${data.id}">
+ ${render_dataset( data, data.hid, show_deleted_on_refresh = show_deleted, user_owns_dataset=user_owns_history )}
+ </div>
+ </td>
+ <td class="annotation">${data.annotation}</td>
+ %endif
+ </tr>
+ %endfor
+ </table>
<div class="infomessagesmall" id="emptyHistoryMessage" style="display:none;">
%endif
${_("Your history is empty. Click 'Get Data' on the left pane to start")}
diff -r c6fe590de8d7 -r 7e5797d6b584 templates/history/embed.mako
--- a/templates/history/embed.mako Sun Feb 14 16:03:54 2010 -0500
+++ b/templates/history/embed.mako Mon Feb 15 09:02:58 2010 -0500
@@ -4,9 +4,6 @@
%>
<%def name="content( history, datasets )">
- %if annotation:
- <div class='annotation'>${annotation}</div>
- %endif
<ul>
<% num_datasets = len ( datasets ) %>
<li>${num_datasets} dataset${iff( num_datasets != 1, "s", "" )}
diff -r c6fe590de8d7 -r 7e5797d6b584 templates/workflow/display.mako
--- a/templates/workflow/display.mako Sun Feb 14 16:03:54 2010 -0500
+++ b/templates/workflow/display.mako Mon Feb 15 09:02:58 2010 -0500
@@ -1,6 +1,9 @@
<%inherit file="/display_base.mako"/>
-<%! from galaxy.tools.parameters import DataToolParameter, RuntimeValue %>
+<%!
+ from galaxy.tools.parameters import DataToolParameter, RuntimeValue
+ from galaxy.web import form_builder
+%>
<%def name="stylesheets()">
${parent.stylesheets()}
@@ -21,10 +24,9 @@
<% repeat_values = values[input.name] %>
%for i in range( len( repeat_values ) ):
<div class="repeat-group-item">
- <% index = repeat_values[i]['__index__'] %>
- <div class="form-title-row"><b>${input.title} ${i + 1}</b></div>
- ${do_inputs( input.inputs, repeat_values[ i ], prefix + input.name + "_" + str(index) + "|", step, other_values )}
-
+ <% index = repeat_values[i]['__index__'] %>
+ <div class="form-title-row"><b>${input.title} ${i + 1}</b></div>
+ ${do_inputs( input.inputs, repeat_values[ i ], prefix + input.name + "_" + str(index) + "|", step, other_values )}
</div>
%endfor
</div>
@@ -37,7 +39,7 @@
%else:
${row_for_param( input, values[ input.name ], other_values, prefix, step )}
%endif
- %endfor
+ %endfor
</%def>
<%def name="row_for_param( param, value, other_values, prefix, step )">
@@ -58,12 +60,18 @@
other_values = {}
value = other_values[ param.name ] = param.get_initial_value( t, other_values )
%>
- ${param.get_html_field( t, value, other_values ).get_html( str(step.id) + "|" + prefix )}
+ ## For display, do not show input elements.
+ <% html_field = param.get_html_field( t, value, other_values ) %>
+ %if type( html_field ) in [ form_builder.SelectField ]:
+ <i>select at runtime</i>
+ %else:
+ ${html_field.get_html( str(step.id) + "|" + prefix )}
+ %endif
%endif
%else:
${param.value_to_display_text( value, app )}
%endif
- </div>
+ </div>
</div>
</%def>
@@ -72,13 +80,15 @@
%if workflow.user != trans.get_user():
<a href="${h.url_for( controller='/workflow', action='imp', id=trans.security.encode_id(workflow.id) )}">import and start using workflow</a>
%else:
- your workflow
+ import and start using workflow
%endif
</%def>
<%def name="render_item( workflow, steps )">
- <h2>${workflow.name | h}</h2>
- %for i, step in enumerate( steps ):
+ <table class="annotated-item">
+ <tr><th>Step</th><th class="annotation">Description/Notes</th></tr>
+ %for i, step in enumerate( steps ):
+ <tr><td>
%if step.type == 'tool' or step.type is None:
<% tool = app.toolbox.tools_by_id[step.tool_id] %>
<div class="toolForm">
@@ -88,14 +98,18 @@
</div>
</div>
%else:
+ ## TODO: always input dataset?
<% module = step.module %>
<div class="toolForm">
<div class="toolFormTitle">Step ${int(step.order_index)+1}: ${module.name}</div>
<div class="toolFormBody">
- ##Need to do more work to print only dataset label and not combo box as well.
- ##${do_inputs( module.get_runtime_inputs(), step.state.inputs, "", step )}
+ ${do_inputs( module.get_runtime_inputs(), step.state.inputs, "", step )}
</div>
</div>
%endif
+ </td>
+ <td class="annotation">${step.annotation}</td>
+ </tr>
%endfor
+ </table>
</%def>
\ No newline at end of file
diff -r c6fe590de8d7 -r 7e5797d6b584 templates/workflow/embed.mako
--- a/templates/workflow/embed.mako Sun Feb 14 16:03:54 2010 -0500
+++ b/templates/workflow/embed.mako Mon Feb 15 09:02:58 2010 -0500
@@ -4,9 +4,6 @@
%>
<%def name="content( workflow, steps )">
- %if annotation:
- <div class='annotation'>${annotation}</div>
- %endif
<ul>
<% num_steps = len ( steps ) %>
<li>${num_steps} step${iff( num_steps != 1, "s", "" )}
1
0
details: http://www.bx.psu.edu/hg/galaxy/rev/c6fe590de8d7
changeset: 3389:c6fe590de8d7
user: Kanwei Li <kanwei(a)gmail.com>
date: Sun Feb 14 16:03:54 2010 -0500
description:
new tipsy works with jQuery 1.4
diffstat:
static/scripts/jquery.tipsy.js | 60 ++++------------------------------
static/scripts/packed/jquery.tipsy.js | 2 +-
2 files changed, 9 insertions(+), 53 deletions(-)
diffs (104 lines):
diff -r aae17e597791 -r c6fe590de8d7 static/scripts/jquery.tipsy.js
--- a/static/scripts/jquery.tipsy.js Sat Feb 13 19:13:54 2010 -0500
+++ b/static/scripts/jquery.tipsy.js Sun Feb 14 16:03:54 2010 -0500
@@ -38,72 +38,27 @@
tip.find('.tipsy-inner')[opts.html ? 'html' : 'text'](title || opts.fallback);
-
var pos = $.extend({}, $(self).offset(), {width: self.offsetWidth, height: self.offsetHeight});
tip.get(0).className = 'tipsy'; // reset classname in case of dynamic gravity
tip.remove().css({top: 0, left: 0, visibility: 'hidden', display: 'block'}).appendTo(document.body);
-
- tip.css( { width: tip.width() + 1, height: tip.height() } );
-
var actualWidth = tip[0].offsetWidth, actualHeight = tip[0].offsetHeight;
var gravity = (typeof opts.gravity == 'function') ? opts.gravity.call(self) : opts.gravity;
- var top, left;
- switch (gravity.charAt(0)) {
+ switch (gravity.charAt(0)) {
case 'n':
- top = pos.top + pos.height;
- left = pos.left + pos.width / 2 - actualWidth / 2;
- tip.addClass('tipsy-north');
+ tip.css({top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}).addClass('tipsy-north');
break;
case 's':
- top = pos.top - actualHeight;
- left = pos.left + pos.width / 2 - actualWidth / 2;
- tip.addClass('tipsy-south');
+ tip.css({top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}).addClass('tipsy-south');
break;
case 'e':
- top = pos.top + pos.height / 2 - actualHeight / 2;
- left = pos.left - actualWidth;
- tip.addClass('tipsy-east');
+ tip.css({top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}).addClass('tipsy-east');
break;
case 'w':
- top = pos.top + pos.height / 2 - actualHeight / 2;
- left = pos.left + pos.width;
- tip.addClass('tipsy-west');
+ tip.css({top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}).addClass('tipsy-west');
break;
}
- // Shift if off screen
- var window = $(window);
-
- top = Math.max( top, window.scrollTop() );
- top = Math.min( top, window.scrollTop() + window.height() - tip.outerHeight() );
-
- var left_shift = 0;
- if ( left < window.scrollLeft() ) {
- left_shift = left - window.scrollLeft();
- }
- var t = window.scrollLeft() + window.width() - tip.outerWidth();
- if ( left > t ) {
- left_shift = left - t;
- }
-
- left -= left_shift;
-
- tip.css( { left: left, top: top } );
-
- // Shift background to center over element (not implemented for east/west)
- switch (gravity.charAt(0)) {
- case 'n':
- tip.css( 'background-position', - ( 250 - tip.outerWidth() / 2 ) + left_shift + "px top" );
- break;
- case 's':
- tip.css( 'background-position', - ( 250 - tip.outerWidth() / 2 ) + left_shift + "px bottom" );
- break;
- case 'e':
- break;
- case 'w':
- break;
- }
-
+
if (opts.fade) {
tip.stop().css({opacity: 0, display: 'block', visibility: 'visible'}).animate({opacity: opts.opacity});
} else {
@@ -116,7 +71,7 @@
var self = this;
clearTimeout(timeout);
setTimeout(function() {
- if ($.data(this, 'cancel.tipsy')) { return; }
+ if ($.data(this, 'cancel.tipsy')) return;
var tip = $.data(self, 'active.tipsy');
if (opts.fade) {
tip.stop().fadeOut(function() { $(this).remove(); });
@@ -159,3 +114,4 @@
};
})(jQuery);
+
diff -r aae17e597791 -r c6fe590de8d7 static/scripts/packed/jquery.tipsy.js
--- a/static/scripts/packed/jquery.tipsy.js Sat Feb 13 19:13:54 2010 -0500
+++ b/static/scripts/packed/jquery.tipsy.js Sun Feb 14 16:03:54 2010 -0500
@@ -1,1 +1,1 @@
-(function(b){function a(c){if(c.attr("title")||typeof(c.attr("original-title"))!="string"){c.attr("original-title",c.attr("title")||"").removeAttr("title")}}b.fn.tipsy=function(c){c=b.extend({},b.fn.tipsy.defaults,c);return this.each(function(){a(b(this));var d=b.fn.tipsy.elementOptions(this,c);var e=null;b(this).hover(function(){var f=this;e=setTimeout(function(){b.data(f,"cancel.tipsy",true);var o=b.data(f,"active.tipsy");if(!o){o=b('<div class="tipsy"><div class="tipsy-inner"/></div>');o.css({position:"absolute",zIndex:100000});b.data(f,"active.tipsy",o)}a(b(f));var n;if(typeof d.title=="string"){n=b(f).attr(d.title=="title"?"original-title":d.title)}else{if(typeof d.title=="function"){n=d.title.call(f)}}o.find(".tipsy-inner")[d.html?"html":"text"](n||d.fallback);var l=b.extend({},b(f).offset(),{width:f.offsetWidth,height:f.offsetHeight});o.get(0).className="tipsy";o.remove().css({top:0,left:0,visibility:"hidden",display:"block"}).appendTo(document.body);o.css({width:o.wi
dth()+1,height:o.height()});var h=o[0].offsetWidth,j=o[0].offsetHeight;var q=(typeof d.gravity=="function")?d.gravity.call(f):d.gravity;var m,i;switch(q.charAt(0)){case"n":m=l.top+l.height;i=l.left+l.width/2-h/2;o.addClass("tipsy-north");break;case"s":m=l.top-j;i=l.left+l.width/2-h/2;o.addClass("tipsy-south");break;case"e":m=l.top+l.height/2-j/2;i=l.left-h;o.addClass("tipsy-east");break;case"w":m=l.top+l.height/2-j/2;i=l.left+l.width;o.addClass("tipsy-west");break}var k=b(k);m=Math.max(m,k.scrollTop());m=Math.min(m,k.scrollTop()+k.height()-o.outerHeight());var g=0;if(i<k.scrollLeft()){g=i-k.scrollLeft()}var p=k.scrollLeft()+k.width()-o.outerWidth();if(i>p){g=i-p}i-=g;o.css({left:i,top:m});switch(q.charAt(0)){case"n":o.css("background-position",-(250-o.outerWidth()/2)+g+"px top");break;case"s":o.css("background-position",-(250-o.outerWidth()/2)+g+"px bottom");break;case"e":break;case"w":break}if(d.fade){o.stop().css({opacity:0,display:"block",visibility:"visible"}).animate({o
pacity:d.opacity})}else{o.css({visibility:"visible",opacity:d.opacity})}},d.delayIn)},function(){b.data(this,"cancel.tipsy",false);var f=this;clearTimeout(e);setTimeout(function(){if(b.data(this,"cancel.tipsy")){return}var g=b.data(f,"active.tipsy");if(d.fade){g.stop().fadeOut(function(){b(this).remove()})}else{if(g){g.remove()}}},d.delayOut)})})};b.fn.tipsy.elementOptions=function(d,c){return b.metadata?b.extend({},c,b(d).metadata()):c};b.fn.tipsy.defaults={delayIn:0,delayOut:100,fade:false,fallback:"",gravity:"n",html:false,opacity:0.8,title:"title"};b.fn.tipsy.autoNS=function(){return b(this).offset().top>(b(document).scrollTop()+b(window).height()/2)?"s":"n"};b.fn.tipsy.autoWE=function(){return b(this).offset().left>(b(document).scrollLeft()+b(window).width()/2)?"e":"w"}})(jQuery);
\ No newline at end of file
+(function(b){function a(c){if(c.attr("title")||typeof(c.attr("original-title"))!="string"){c.attr("original-title",c.attr("title")||"").removeAttr("title")}}b.fn.tipsy=function(c){c=b.extend({},b.fn.tipsy.defaults,c);return this.each(function(){a(b(this));var d=b.fn.tipsy.elementOptions(this,c);var e=null;b(this).hover(function(){var f=this;e=setTimeout(function(){b.data(f,"cancel.tipsy",true);var g=b.data(f,"active.tipsy");if(!g){g=b('<div class="tipsy"><div class="tipsy-inner"/></div>');g.css({position:"absolute",zIndex:100000});b.data(f,"active.tipsy",g)}a(b(f));var i;if(typeof d.title=="string"){i=b(f).attr(d.title=="title"?"original-title":d.title)}else{if(typeof d.title=="function"){i=d.title.call(f)}}g.find(".tipsy-inner")[d.html?"html":"text"](i||d.fallback);var l=b.extend({},b(f).offset(),{width:f.offsetWidth,height:f.offsetHeight});g.get(0).className="tipsy";g.remove().css({top:0,left:0,visibility:"hidden",display:"block"}).appendTo(document.body);var h=g[0].offset
Width,k=g[0].offsetHeight;var j=(typeof d.gravity=="function")?d.gravity.call(f):d.gravity;switch(j.charAt(0)){case"n":g.css({top:l.top+l.height,left:l.left+l.width/2-h/2}).addClass("tipsy-north");break;case"s":g.css({top:l.top-k,left:l.left+l.width/2-h/2}).addClass("tipsy-south");break;case"e":g.css({top:l.top+l.height/2-k/2,left:l.left-h}).addClass("tipsy-east");break;case"w":g.css({top:l.top+l.height/2-k/2,left:l.left+l.width}).addClass("tipsy-west");break}if(d.fade){g.stop().css({opacity:0,display:"block",visibility:"visible"}).animate({opacity:d.opacity})}else{g.css({visibility:"visible",opacity:d.opacity})}},d.delayIn)},function(){b.data(this,"cancel.tipsy",false);var f=this;clearTimeout(e);setTimeout(function(){if(b.data(this,"cancel.tipsy")){return}var g=b.data(f,"active.tipsy");if(d.fade){g.stop().fadeOut(function(){b(this).remove()})}else{if(g){g.remove()}}},d.delayOut)})})};b.fn.tipsy.elementOptions=function(d,c){return b.metadata?b.extend({},c,b(d).metadata()):c}
;b.fn.tipsy.defaults={delayIn:0,delayOut:100,fade:false,fallback:"",gravity:"n",html:false,opacity:0.8,title:"title"};b.fn.tipsy.autoNS=function(){return b(this).offset().top>(b(document).scrollTop()+b(window).height()/2)?"s":"n"};b.fn.tipsy.autoWE=function(){return b(this).offset().left>(b(document).scrollLeft()+b(window).width()/2)?"e":"w"}})(jQuery);
\ No newline at end of file
1
0
15 Feb '10
details: http://www.bx.psu.edu/hg/galaxy/rev/aae17e597791
changeset: 3388:aae17e597791
user: Kelly Vincent <kpvincent(a)bx.psu.edu>
date: Sat Feb 13 19:13:54 2010 -0500
description:
Updated two test files so BWA wrapper tool will pass
diffstat:
test-data/bwa_wrapper_out2.sam | 2 +-
test-data/bwa_wrapper_out3.sam | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diffs (14 lines):
diff -r fb48e02e1610 -r aae17e597791 test-data/bwa_wrapper_out2.sam
--- a/test-data/bwa_wrapper_out2.sam Fri Feb 12 17:10:52 2010 -0500
+++ b/test-data/bwa_wrapper_out2.sam Sat Feb 13 19:13:54 2010 -0500
@@ -1,1 +1,1 @@
-081017-and-081020:1:1:1715:1759 16 phiX 322 25 36M * 0 0 GATATTTTAAAGGAGCGTGGATTACTATCTGAGTCC B&&I13A$G$*%$IIIIIII9(.+5$IIIIIII#II XT:A:U NM:i:2 X0:i:1 X1:i:0 XM:i:2 XO:i:0 XG:i:0 MD:Z:2C8A24
+081017-and-081020:1:1:1715:1759 16 phiX 322 25 36M * 0 0 GATATTTTAAAGGAGCGTGGATTACTATCTGAGTCC B&&I13A$G$*%$IIIIIII9(.+5$IIIIIII#II XT:A:U NM:i:2 X0:i:1 XM:i:2 XO:i:0 XG:i:0 MD:Z:2C8A24
diff -r fb48e02e1610 -r aae17e597791 test-data/bwa_wrapper_out3.sam
--- a/test-data/bwa_wrapper_out3.sam Fri Feb 12 17:10:52 2010 -0500
+++ b/test-data/bwa_wrapper_out3.sam Sat Feb 13 19:13:54 2010 -0500
@@ -1,2 +1,2 @@
-081017-and-081020:1:1:1715:1759 113 PHIX174 322 25 18M = 340 18 GATATTTTAAAGGAGCGT B&&I13A$G$*%$IIIII XT:A:U NM:i:2 SM:i:25 AM:i:25 X0:i:1 X1:i:0 XM:i:2 XO:i:0 XG:i:0 MD:Z:2C8A6
-081017-and-081020:1:1:1715:1759 177 PHIX174 340 37 18M = 322 -18 GGATTACTATCTGAGTCC II9(.+5$IIIIIII#II XT:A:U NM:i:0 SM:i:37 AM:i:25 X0:i:1 X1:i:0 XM:i:0 XO:i:0 XG:i:0 MD:Z:18
+081017-and-081020:1:1:1715:1759 113 PHIX174 322 25 18M = 340 18 GATATTTTAAAGGAGCGT B&&I13A$G$*%$IIIII XT:A:U NM:i:2 SM:i:25 AM:i:25 X0:i:1 XM:i:2 XO:i:0 XG:i:0 MD:Z:2C8A6
+081017-and-081020:1:1:1715:1759 177 PHIX174 340 37 18M = 322 -18 GGATTACTATCTGAGTCC II9(.+5$IIIIIII#II XT:A:U NM:i:0 SM:i:37 AM:i:25 X0:i:1 XM:i:0 XO:i:0 XG:i:0 MD:Z:18
1
0
details: http://www.bx.psu.edu/hg/galaxy/rev/fb48e02e1610
changeset: 3387:fb48e02e1610
user: rerla@localhost
date: Fri Feb 12 17:10:52 2010 -0500
description:
merge
diffstat:
lib/galaxy/model/mapping.py | 15 +
lib/galaxy/web/base/controller.py | 141 ++++++++++-
lib/galaxy/web/controllers/dataset.py | 64 ++--
lib/galaxy/web/controllers/history.py | 37 +-
lib/galaxy/web/controllers/page.py | 147 +++++++++--
lib/galaxy/web/controllers/requests_admin.py | 46 ++-
lib/galaxy/web/controllers/root.py | 13 +-
lib/galaxy/web/controllers/workflow.py | 96 +++----
scripts/galaxy_messaging/server/data_transfer.py | 4 +-
static/images/fugue/pencil.png | 0
static/images/fugue/sticky-note-text.png | 0
static/images/fugue/tag-label.png | 0
static/images/fugue/toggle-expand.png | 0
static/images/fugue/toggle.png | 0
static/images/pencil.png | 0
static/images/sticky-note-text.png | 0
static/images/tag-label.png | 0
static/june_2007_style/base.css.tmpl | 12 +-
static/june_2007_style/blue/base.css | 6 +-
static/june_2007_style/blue/embed_item.css | 4 +
static/june_2007_style/embed_item.css.tmpl | 24 ++
templates/dataset/display.mako | 3 +-
templates/dataset/embed.mako | 13 +
templates/dataset/item_content.mako | 3 +
templates/display_base.mako | 28 +-
templates/display_common.mako | 24 ++
templates/embed_base.mako | 38 +++
templates/history/embed.mako | 15 +
templates/history/item_content.mako | 3 +
templates/page/display.mako | 58 ++++-
templates/page/editor.mako | 265 +++++++++++++++++++---
templates/page/wymiframe.mako | 2 +-
templates/sharing_base.mako | 2 +-
templates/workflow/display.mako | 1 -
templates/workflow/embed.mako | 15 +
templates/workflow/item_content.mako | 3 +
36 files changed, 832 insertions(+), 250 deletions(-)
diffs (1789 lines):
diff -r 8d9c8cf65dee -r fb48e02e1610 lib/galaxy/model/mapping.py
--- a/lib/galaxy/model/mapping.py Fri Feb 12 17:10:05 2010 -0500
+++ b/lib/galaxy/model/mapping.py Fri Feb 12 17:10:52 2010 -0500
@@ -980,6 +980,11 @@
tags=relation( HistoryTagAssociation, order_by=HistoryTagAssociation.table.c.id, backref="histories" ),
annotations=relation( HistoryAnnotationAssociation, order_by=HistoryAnnotationAssociation.table.c.id, backref="histories" ) )
)
+
+# Set up proxy so that
+# History.users_shared_with_dot_users
+# returns a list of User objects.
+History.users_shared_with_dot_users = association_proxy( 'users_shared_with', 'user' )
assign_mapper( context, HistoryUserShareAssociation, HistoryUserShareAssociation.table,
properties=dict( user=relation( User, backref='histories_shared_by_others' ),
@@ -1271,6 +1276,11 @@
tags=relation( StoredWorkflowTagAssociation, order_by=StoredWorkflowTagAssociation.table.c.id, backref="stored_workflows" ),
annotations=relation( StoredWorkflowAnnotationAssociation, order_by=StoredWorkflowAnnotationAssociation.table.c.id, backref="stored_workflows" ) )
)
+
+# Set up proxy so that
+# StoredWorkflow.users_shared_with_dot_users
+# returns a list of User objects.
+StoredWorkflow.users_shared_with_dot_users = association_proxy( 'users_shared_with', 'user' )
assign_mapper( context, StoredWorkflowUserShareAssociation, StoredWorkflowUserShareAssociation.table,
properties=dict( user=relation( User, backref='workflows_shared_by_others' ),
@@ -1296,6 +1306,11 @@
tags=relation(PageTagAssociation, order_by=PageTagAssociation.table.c.id, backref="pages")
) )
+# Set up proxy so that
+# Page.users_shared_with_dot_users
+# returns a list of User objects.
+Page.users_shared_with_dot_users = association_proxy( 'users_shared_with', 'user' )
+
assign_mapper( context, PageUserShareAssociation, PageUserShareAssociation.table,
properties=dict( user=relation( User, backref='pages_shared_by_others' ),
page=relation( Page, backref='users_shared_with' )
diff -r 8d9c8cf65dee -r fb48e02e1610 lib/galaxy/web/base/controller.py
--- a/lib/galaxy/web/base/controller.py Fri Feb 12 17:10:05 2010 -0500
+++ b/lib/galaxy/web/base/controller.py Fri Feb 12 17:10:52 2010 -0500
@@ -8,7 +8,7 @@
from galaxy import config, tools, web, model, util
from galaxy.web import error, form, url_for
from galaxy.model.orm import *
-from galaxy.web.framework.helpers import grids
+from galaxy.workflow.modules import *
from Cheetah.Template import Template
@@ -27,22 +27,6 @@
"""Returns the application toolbox"""
return self.app.toolbox
- def get_history( self, trans, id, check_ownership=True ):
- """Get a History from the database by id, verifying ownership."""
- # Load history from database
- id = trans.security.decode_id( id )
- history = trans.sa_session.query( model.History ).get( id )
- if not history:
- err+msg( "History not found" )
- if check_ownership:
- # Verify ownership
- user = trans.get_user()
- if not user:
- error( "Must be logged in to manage histories" )
- if history.user != user:
- error( "History is not owned by current user" )
- return history
-
def get_class( self, class_name ):
""" Returns the class object that a string denotes. Without this method, we'd have to do eval(<class_name>). """
if class_name == 'History':
@@ -107,9 +91,124 @@
return True
Root = BaseController
+
+class SharableItemSecurity:
+ """ Mixin for handling security for sharable items. """
+
+ def security_check( self, user, item, check_ownership=False, check_accessible=False ):
+ """ Security checks for an item: checks if (a) user owns item or (b) item is accessible to user. """
+ if check_ownership:
+ # Verify ownership.
+ if not user:
+ error( "Must be logged in to manage Galaxy items" )
+ if item.user != user:
+ error( "%s is not owned by current user" % item.__class__.__name__ )
+ if check_accessible:
+ # Verify accessible.
+ if ( item.user != user ) and ( not item.importable ) and ( user not in item.users_shared_with_dot_users ):
+ raise "hi"
+ error( "%s is not accessible by current user" % item.__class__.__name__ )
+ return item
+
+class UsesHistoryDatasetAssociation:
+ """ Mixin for controllers that use HistoryDatasetAssociation objects. """
+
+ def get_dataset( self, trans, dataset_id, check_accessible=True ):
+ """ Get an HDA object by id. """
+ # DEPRECATION: We still support unencoded ids for backward compatibility
+ try:
+ dataset_id = int( dataset_id )
+ except ValueError:
+ dataset_id = trans.security.decode_id( dataset_id )
+ data = trans.sa_session.query( model.HistoryDatasetAssociation ).get( dataset_id )
+ if not data:
+ raise paste.httpexceptions.HTTPRequestRangeNotSatisfiable( "Invalid dataset id: %s." % str( dataset_id ) )
+ if check_accessible:
+ current_user_roles = trans.get_current_user_roles()
+ if trans.app.security_agent.can_access_dataset( current_user_roles, data.dataset ):
+ if data.state == trans.model.Dataset.states.UPLOAD:
+ return trans.show_error_message( "Please wait until this dataset finishes uploading before attempting to view it." )
+ else:
+ error( "You are not allowed to access this dataset" )
+ return data
+
+ def get_data( self, dataset, preview=True ):
+ """ Gets a dataset's data. """
+ # Get data from file, truncating if necessary.
+ truncated = False
+ dataset_data = None
+ if os.path.exists( dataset.file_name ):
+ max_peek_size = 1000000 # 1 MB
+ if preview and os.stat( dataset.file_name ).st_size > max_peek_size:
+ dataset_data = open( dataset.file_name ).read(max_peek_size)
+ truncated = True
+ else:
+ dataset_data = open( dataset.file_name ).read(max_peek_size)
+ truncated = False
+ return truncated, dataset_data
+
+class UsesStoredWorkflow( SharableItemSecurity ):
+ """ Mixin for controllers that use StoredWorkflow objects. """
+
+ def get_stored_workflow( self, trans, id, check_ownership=True, check_accessible=False ):
+ """ Get a StoredWorkflow from the database by id, verifying ownership. """
+ # Load workflow from database
+ id = trans.security.decode_id( id )
+ stored = trans.sa_session.query( model.StoredWorkflow ).get( id )
+ if not stored:
+ error( "Workflow not found" )
+ else:
+ return self.security_check( trans.get_user(), stored, check_ownership, check_accessible )
+
+ def get_stored_workflow_steps( self, trans, stored_workflow ):
+ """ Restores states for a stored workflow's steps. """
+ for step in stored_workflow.latest_workflow.steps:
+ if step.type == 'tool' or step.type is None:
+ # Restore the tool state for the step
+ module = module_factory.from_workflow_step( trans, step )
+ # Any connected input needs to have value DummyDataset (these
+ # are not persisted so we need to do it every time)
+ module.add_dummy_datasets( connections=step.input_connections )
+ # Store state with the step
+ step.module = module
+ step.state = module.state
+ # Error dict
+ if step.tool_errors:
+ errors[step.id] = step.tool_errors
+ else:
+ ## Non-tool specific stuff?
+ step.module = module_factory.from_workflow_step( trans, step )
+ step.state = step.module.get_runtime_state()
+ # Connections by input name
+ step.input_connections_by_name = dict( ( conn.input_name, conn ) for conn in step.input_connections )
+
+class UsesHistory( SharableItemSecurity ):
+ """ Mixin for controllers that use History objects. """
+
+ def get_history( self, trans, id, check_ownership=True, check_accessible=False ):
+ """Get a History from the database by id, verifying ownership."""
+ # Load history from database
+ id = trans.security.decode_id( id )
+ history = trans.sa_session.query( model.History ).get( id )
+ if not history:
+ err+msg( "History not found" )
+ else:
+ return self.security_check( trans.get_user(), history, check_ownership, check_accessible )
+
+ def get_history_datasets( self, trans, history, show_deleted=False ):
+ """ Returns history's datasets. """
+ query = trans.sa_session.query( model.HistoryDatasetAssociation ) \
+ .filter( model.HistoryDatasetAssociation.history == history ) \
+ .options( eagerload( "children" ) ) \
+ .join( "dataset" ).filter( model.Dataset.purged == False ) \
+ .options( eagerload_all( "dataset.actions" ) ) \
+ .order_by( model.HistoryDatasetAssociation.hid )
+ if not show_deleted:
+ query = query.filter( model.HistoryDatasetAssociation.deleted == False )
+ return query.all()
class Sharable:
- """ Mixin for a controller that manages and item that can be shared. """
+ """ Mixin for a controller that manages an item that can be shared. """
# Implemented methods.
@web.expose
@@ -146,6 +245,12 @@
""" Returns item's name and link. """
pass
+ @web.expose
+ @web.require_login("get item content asynchronously")
+ def get_item_content_async( self, trans, id ):
+ """ Returns item content in HTML format. """
+ pass
+
# Helper methods.
def _make_item_accessible( self, sa_session, item ):
diff -r 8d9c8cf65dee -r fb48e02e1610 lib/galaxy/web/controllers/dataset.py
--- a/lib/galaxy/web/controllers/dataset.py Fri Feb 12 17:10:05 2010 -0500
+++ b/lib/galaxy/web/controllers/dataset.py Fri Feb 12 17:10:52 2010 -0500
@@ -110,7 +110,7 @@
# a user relation.
return query.select_from( model.HistoryDatasetAssociation.table.join( model.History.table ) ).filter( model.History.user == trans.user )
-class DatasetInterface( BaseController ):
+class DatasetInterface( BaseController, UsesHistoryDatasetAssociation ):
stored_list_grid = HistoryDatasetAssociationListGrid()
@@ -293,9 +293,18 @@
@web.require_login( "use Galaxy datasets" )
def get_name_and_link_async( self, trans, id=None ):
""" Returns dataset's name and link. """
- dataset = self.get_data( trans, id )
+ dataset = self.get_dataset( trans, id )
return_dict = { "name" : dataset.name, "link" : url_for( action="display_by_username_and_slug", username=dataset.history.user.username, slug=trans.security.encode_id( dataset.id ) ) }
return return_dict
+
+ @web.expose
+ def get_embed_html_async( self, trans, id ):
+ """ Returns HTML for embedding a dataset in a page. """
+
+ # TODO: user should be able to embed any item he has access to. see display_by_username_and_slug for security code.
+ dataset = self.get_dataset( trans, id )
+ if dataset:
+ return "Embedded Dataset '%s'" % dataset.name
@web.expose
@web.require_login( "use Galaxy datasets" )
@@ -306,20 +315,26 @@
@web.expose
def display_by_username_and_slug( self, trans, username, slug, preview=True ):
""" Display dataset by username and slug; because datasets do not yet have slugs, the slug is the dataset's id. """
- data = self.get_data( trans, slug )
- if data:
- # Get data from file, truncating if necessary.
- if os.path.exists( data.file_name ):
- max_peek_size = 1000000 # 1 MB
- if preview and os.stat( data.file_name ).st_size > max_peek_size:
- dataset_data = open( data.file_name ).read(max_peek_size)
- truncated = True
- else:
- dataset_data = open( data.file_name ).read(max_peek_size)
- truncated = False
- return trans.fill_template_mako( "dataset/display.mako", item=data, item_data=dataset_data, truncated=truncated )
+ dataset = self.get_dataset( trans, slug )
+ if dataset:
+ truncated, dataset_data = self.get_data( dataset, preview )
+ return trans.fill_template_mako( "/dataset/display.mako", item=dataset, item_data=dataset_data, truncated=truncated )
else:
raise web.httpexceptions.HTTPNotFound()
+
+ @web.expose
+ @web.require_login("get item content asynchronously")
+ def get_item_content_async( self, trans, id ):
+ """ Returns item content in HTML format. """
+
+ dataset = self.get_dataset( trans, id )
+ if dataset is None:
+ raise web.httpexceptions.HTTPNotFound()
+ truncated, dataset_data = self.get_data( dataset, preview=True )
+ # Get annotation.
+ annotation = self.get_item_annotation_str( trans.sa_session, trans.get_user(), dataset )
+ return trans.stream_template_mako( "/dataset/item_content.mako", item=dataset, item_data=dataset_data, truncated=truncated )
+
@web.expose
def display_at( self, trans, dataset_id, filename=None, **kwd ):
@@ -513,23 +528,4 @@
new_history_name = new_history_name,
done_msg = done_msg,
error_msg = error_msg,
- refresh_frames = refresh_frames )
-
- def get_data( self, trans, dataset_id, check_user_can_access=True ):
- """ Get an HDA from the database by id, verifying that user can access dataset."""
- # DEPRECATION: We still support unencoded ids for backward compatibility
- try:
- dataset_id = int( dataset_id )
- except ValueError:
- dataset_id = trans.security.decode_id( dataset_id )
- data = trans.sa_session.query( model.HistoryDatasetAssociation ).get( dataset_id )
- if not data:
- raise paste.httpexceptions.HTTPRequestRangeNotSatisfiable( "Invalid dataset id: %s." % str( dataset_id ) )
- if check_user_can_access:
- current_user_roles = trans.get_current_user_roles()
- if trans.app.security_agent.can_access_dataset( current_user_roles, data.dataset ):
- if data.state == trans.model.Dataset.states.UPLOAD:
- return trans.show_error_message( "Please wait until this dataset finishes uploading before attempting to view it." )
- else:
- error( "You are not allowed to access this dataset" )
- return data
\ No newline at end of file
+ refresh_frames = refresh_frames )
\ No newline at end of file
diff -r 8d9c8cf65dee -r fb48e02e1610 lib/galaxy/web/controllers/history.py
--- a/lib/galaxy/web/controllers/history.py Fri Feb 12 17:10:05 2010 -0500
+++ b/lib/galaxy/web/controllers/history.py Fri Feb 12 17:10:52 2010 -0500
@@ -146,7 +146,7 @@
# A public history is published, has a slug, and is not deleted.
return query.filter( self.model_class.published == True ).filter( self.model_class.slug != None ).filter( self.model_class.deleted == False )
-class HistoryController( BaseController, Sharable ):
+class HistoryController( BaseController, Sharable, UsesHistory ):
@web.expose
def index( self, trans ):
return ""
@@ -406,6 +406,21 @@
history.slug = new_slug
trans.sa_session.flush()
return history.slug
+
+ @web.expose
+ @web.require_login("get item content asynchronously")
+ def get_item_content_async( self, trans, id ):
+ """ Returns item content in HTML format. """
+
+ history = self.get_history( trans, id, False, True )
+ if history is None:
+ raise web.httpexceptions.HTTPNotFound()
+
+ # Get datasets.
+ datasets = self.get_history_datasets( trans, history )
+ # Get annotation.
+ annotation = self.get_item_annotation_str( trans.sa_session, trans.get_user(), history )
+ return trans.stream_template_mako( "/history/item_content.mako", item = history, item_data = datasets )
@web.expose
def name_autocomplete_data( self, trans, q=None, limit=None, timestamp=None ):
@@ -489,17 +504,11 @@
if not trans.user_is_admin and not history_to_view.importable:
error( "Either you are not allowed to view this history or the owner of this history has not made it accessible." )
# View history.
- query = trans.sa_session.query( model.HistoryDatasetAssociation ) \
- .filter( model.HistoryDatasetAssociation.history == history_to_view ) \
- .options( eagerload( "children" ) ) \
- .join( "dataset" ).filter( model.Dataset.purged == False ) \
- .options( eagerload_all( "dataset.actions" ) )
- # Do not show deleted datasets.
- query = query.filter( model.HistoryDatasetAssociation.deleted == False )
+ datasets = self.get_history_datasets( trans, history_to_view )
user_owns_history = ( trans.get_user() == history_to_view.user )
return trans.stream_template_mako( "history/view.mako",
history = history_to_view,
- datasets = query.all(),
+ datasets = datasets,
user_owns_history = user_owns_history,
show_deleted = False )
@@ -521,15 +530,9 @@
raise web.httpexceptions.HTTPNotFound()
# Get datasets.
- query = trans.sa_session.query( model.HistoryDatasetAssociation ) \
- .filter( model.HistoryDatasetAssociation.history == history ) \
- .options( eagerload( "children" ) ) \
- .join( "dataset" ).filter( model.Dataset.purged == False ) \
- .options( eagerload_all( "dataset.actions" ) )
- # Do not show deleted datasets.
- query = query.filter( model.HistoryDatasetAssociation.deleted == False )
+ datasets = self.get_history_datasets( trans, history )
return trans.stream_template_mako( "history/display.mako",
- item = history, item_data = query.all() )
+ item = history, item_data = datasets )
@web.expose
@web.require_login( "share Galaxy histories" )
diff -r 8d9c8cf65dee -r fb48e02e1610 lib/galaxy/web/controllers/page.py
--- a/lib/galaxy/web/controllers/page.py Fri Feb 12 17:10:05 2010 -0500
+++ b/lib/galaxy/web/controllers/page.py Fri Feb 12 17:10:52 2010 -0500
@@ -1,6 +1,6 @@
from galaxy.web.base.controller import *
from galaxy.web.framework.helpers import time_ago, grids
-from galaxy.util.sanitize_html import sanitize_html
+from galaxy.util.sanitize_html import sanitize_html, _BaseHTMLProcessor
from galaxy.util.odict import odict
from galaxy.util.json import from_json_string
@@ -222,7 +222,70 @@
key="free-text-search", visible=False, filterable="standard" )
)
-class PageController( BaseController, Sharable ):
+class _PageContentProcessor( _BaseHTMLProcessor ):
+ """ Processes page content to produce HTML that is suitable for display. For now, processor renders embedded objects. """
+
+ def __init__( self, trans, encoding, type, render_embed_html_fn ):
+ _BaseHTMLProcessor.__init__( self, encoding, type)
+ self.trans = trans
+ self.ignore_content = False
+ self.num_open_tags_for_ignore = 0
+ self.render_embed_html_fn = render_embed_html_fn
+
+ def unknown_starttag( self, tag, attrs ):
+ """ Called for each start tag; attrs is a list of (attr, value) tuples. """
+
+ # If ignoring content, just increment tag count and ignore.
+ if self.ignore_content:
+ self.num_open_tags_for_ignore += 1
+ return
+
+ # Not ignoring tag; look for embedded content.
+ embedded_item = False
+ for attribute in attrs:
+ if ( attribute[0] == "class" ) and ( "embedded-item" in attribute[1].split(" ") ):
+ embedded_item = True
+ break
+ # For embedded content, set ignore flag to ignore current content and add new content for embedded item.
+ if embedded_item:
+ # Set processing attributes to ignore content.
+ self.ignore_content = True
+ self.num_open_tags_for_ignore = 1
+
+ # Insert content for embedded element.
+ for attribute in attrs:
+ name = attribute[0]
+ if name == "id":
+ # ID has form '<class_name>-<encoded_item_id>'
+ item_class, item_id = attribute[1].split("-")
+ embed_html = self.render_embed_html_fn( self.trans, item_class, item_id )
+ self.pieces.append( embed_html )
+ return
+
+ # Default behavior: not ignoring and no embedded content.
+ _BaseHTMLProcessor.unknown_starttag( self, tag, attrs )
+
+ def handle_data( self, text ):
+ """ Called for each block of plain text. """
+ if self.ignore_content:
+ return
+ _BaseHTMLProcessor.handle_data( self, text )
+
+ def unknown_endtag( self, tag ):
+ """ Called for each end tag. """
+
+ # If ignoring content, see if current tag is the end of content to ignore.
+ if self.ignore_content:
+ self.num_open_tags_for_ignore -= 1
+ if self.num_open_tags_for_ignore == 0:
+ # Done ignoring content.
+ self.ignore_content = False
+ return
+
+ # Default behavior:
+ _BaseHTMLProcessor.unknown_endtag( self, tag )
+
+class PageController( BaseController, Sharable, UsesHistory, UsesStoredWorkflow, UsesHistoryDatasetAssociation ):
_page_list = PageListGrid()
_all_published_list = PageAllPublishedGrid()
@@ -520,9 +583,13 @@
# User not logged in, so only way to view page is if it's importable.
page = page_query_base.filter_by( importable=True ).first()
if page is None:
- raise web.httpexceptions.HTTPNotFound()
-
- return trans.fill_template_mako( "page/display.mako", item=page)
+ raise web.httpexceptions.HTTPNotFound()
+
+ # Process page content.
+ processor = _PageContentProcessor( trans, 'utf-8', 'text/html', self._get_embed_html )
+ processor.feed( page.latest_revision.content )
+ page_content = processor.output()
+ return trans.fill_template_mako( "page/display.mako", item=page, item_data=page_content, content_only=True )
@web.expose
@web.require_login( "use Galaxy pages" )
@@ -548,6 +615,15 @@
page.slug = new_slug
trans.sa_session.flush()
return page.slug
+
+ @web.expose
+ def get_embed_html_async( self, trans, id ):
+ """ Returns HTML for embedding a workflow in a page. """
+
+ # TODO: user should be able to embed any item he has access to. see display_by_username_and_slug for security code.
+ page = self.get_page( trans, id )
+ if page:
+ return "Embedded Page '%s'" % page.title
@web.expose
@web.json
@@ -593,43 +669,50 @@
@web.require_login("get annotation table for history")
def get_history_annotation_table( self, trans, id ):
""" Returns HTML for an annotation table for a history. """
-
- # TODO: users should be able to annotate a history if they own it, it is importable, or it is shared with them. This only
- # returns a history if a user owns it.
- history = self.get_history( trans, id, True )
+ history = self.get_history( trans, id, False, True )
if history:
- # TODO: Query taken from root/history; it should be moved either into history or trans object
- # so that it can reused.
- query = trans.sa_session.query( model.HistoryDatasetAssociation ) \
- .filter( model.HistoryDatasetAssociation.history == history ) \
- .options( eagerload( "children" ) ) \
- .join( "dataset" ).filter( model.Dataset.purged == False ) \
- .options( eagerload_all( "dataset.actions" ) ) \
- .order_by( model.HistoryDatasetAssociation.hid )
- # For now, do not show deleted datasets.
- show_deleted = False
- if not show_deleted:
- query = query.filter( model.HistoryDatasetAssociation.deleted == False )
- return trans.fill_template( "page/history_annotation_table.mako", history=history, datasets=query.all(), show_deleted=False )
+ datasets = self.get_history_datasets( trans, history )
+ return trans.fill_template( "page/history_annotation_table.mako", history=history, datasets=datasets, show_deleted=False )
@web.expose
def get_editor_iframe( self, trans ):
""" Returns the document for the page editor's iframe. """
return trans.fill_template( "page/wymiframe.mako" )
- def get_page( self, trans, id, check_ownership=True ):
+ def get_page( self, trans, id, check_ownership=True, check_accessible=False ):
"""Get a page from the database by id, verifying ownership."""
# Load history from database
id = trans.security.decode_id( id )
page = trans.sa_session.query( model.Page ).get( id )
if not page:
- err+msg( "History not found" )
- if check_ownership:
- # Verify ownership
- user = trans.get_user()
- if not user:
- error( "Must be logged in to work with Pages" )
- if page.user != user:
- error( "History is not owned by current user" )
- return page
\ No newline at end of file
+ err+msg( "Page not found" )
+ else:
+ return self.security_check( trans.get_user(), page, check_ownership, check_accessible )
+
+ def _get_embed_html( self, trans, item_class, item_id ):
+ """ Returns HTML for embedding an item in a page. """
+ item_class = self.get_class( item_class )
+ if item_class == model.History:
+ history = self.get_history( trans, item_id, False, True )
+ if history:
+ datasets = self.get_history_datasets( trans, history )
+ annotation = self.get_item_annotation_str( trans.sa_session, history.user, history )
+ return trans.fill_template( "history/embed.mako", item=history, item_data=datasets, annotation=annotation )
+ elif item_class == model.HistoryDatasetAssociation:
+ dataset = self.get_dataset( trans, item_id )
+ if dataset:
+ data = self.get_data( dataset )
+ annotation = self.get_item_annotation_str( trans.sa_session, dataset.history.user, dataset )
+ return trans.fill_template( "dataset/embed.mako", item=dataset, item_data=data, annotation=annotation )
+ elif item_class == model.StoredWorkflow:
+ workflow = self.get_stored_workflow( trans, item_id, False, True )
+ if workflow:
+ self.get_stored_workflow_steps( trans, workflow )
+ annotation = self.get_item_annotation_str( trans.sa_session, workflow.user, workflow )
+ return trans.fill_template( "workflow/embed.mako", item=workflow, item_data=workflow.latest_workflow.steps, annotation=annotation )
+ elif item_class == model.Page:
+ pass
+
+
+
\ No newline at end of file
diff -r 8d9c8cf65dee -r fb48e02e1610 lib/galaxy/web/controllers/requests_admin.py
--- a/lib/galaxy/web/controllers/requests_admin.py Fri Feb 12 17:10:05 2010 -0500
+++ b/lib/galaxy/web/controllers/requests_admin.py Fri Feb 12 17:10:52 2010 -0500
@@ -11,7 +11,7 @@
from sqlalchemy.sql.expression import func, and_
from sqlalchemy.sql import select
import pexpect
-import ConfigParser
+import ConfigParser, threading, time
log = logging.getLogger( __name__ )
@@ -204,6 +204,18 @@
action='create_request_type' ) )
]
+class DataTransferThread(threading.Thread):
+ def __init__(self, **kwargs):
+ threading.Thread.__init__(self, name=kwargs['name'])
+ self.dataset_index = kwargs['dataset_index']
+ self.cmd = kwargs['cmd']
+ def run(self):
+ try:
+ retcode = subprocess.call(self.cmd)
+ except Exception, e:
+ error_msg = "Data transfer failed. " + str(e) + "<br/>"
+ log.debug(error_msg)
+
#
# ---- Request Controller ------------------------------------------------------
@@ -1511,7 +1523,7 @@
trans.sa_session.add( dp )
trans.sa_session.flush()
return datatx_user
-
+
def __start_datatx(self, trans, sample):
# data transfer user
datatx_user = self.__setup_datatx_user(trans, sample.library, sample.folder)
@@ -1542,20 +1554,28 @@
str(index),
trans.security.encode_id(sample.library.id),
trans.security.encode_id(sample.folder.id) )
- # set the transfer status
+# # set the transfer status
sample.dataset_files[index][1] = sample.transfer_status.IN_PROGRESS
trans.sa_session.add( sample )
trans.sa_session.flush()
- try:
- retcode = subprocess.call(cmd)
- except Exception, e:
- error_msg = dfile.split('/')[-1] + ": Data transfer failed. " + str(e) + "<br/>"
- return trans.response.send_redirect( web.url_for( controller='requests_admin',
- action='show_datatx_page',
- sample_id=trans.security.encode_id(sample.id),
- folder_path=os.path.dirname(dfile),
- messagetype='error',
- msg=error_msg))
+ dtt = DataTransferThread(name='thread_'+str(index),
+ dataset_index=index,
+ cmd=cmd)
+ dtt.start()
+# # set the transfer status
+# sample.dataset_files[index][1] = sample.transfer_status.IN_PROGRESS
+# trans.sa_session.add( sample )
+# trans.sa_session.flush()
+# try:
+# retcode = subprocess.call(cmd)
+# except Exception, e:
+# error_msg = dfile.split('/')[-1] + ": Data transfer failed. " + str(e) + "<br/>"
+# return trans.response.send_redirect( web.url_for( controller='requests_admin',
+# action='show_datatx_page',
+# sample_id=trans.security.encode_id(sample.id),
+# folder_path=os.path.dirname(dfile),
+# messagetype='error',
+# msg=error_msg))
# set the sample state to the last state
if sample.current_state().id != sample.request.type.states[-1].id:
event = trans.app.model.SampleEvent(sample, sample.request.type.states[-1],
diff -r 8d9c8cf65dee -r fb48e02e1610 lib/galaxy/web/controllers/root.py
--- a/lib/galaxy/web/controllers/root.py Fri Feb 12 17:10:05 2010 -0500
+++ b/lib/galaxy/web/controllers/root.py Fri Feb 12 17:10:52 2010 -0500
@@ -10,7 +10,7 @@
log = logging.getLogger( __name__ )
-class RootController( BaseController ):
+class RootController( BaseController, UsesHistory ):
@web.expose
def default(self, trans, target1=None, target2=None, **kwd):
@@ -69,18 +69,11 @@
return trans.fill_template_mako( "root/history_as_xml.mako", history=history, show_deleted=util.string_as_bool( show_deleted ) )
else:
show_deleted = util.string_as_bool( show_deleted )
- query = trans.sa_session.query( model.HistoryDatasetAssociation ) \
- .filter( model.HistoryDatasetAssociation.history == history ) \
- .options( eagerload( "children" ) ) \
- .join( "dataset" ).filter( model.Dataset.purged == False ) \
- .options( eagerload_all( "dataset.actions" ) ) \
- .order_by( model.HistoryDatasetAssociation.hid )
- if not show_deleted:
- query = query.filter( model.HistoryDatasetAssociation.deleted == False )
+ datasets = self.get_history_datasets( trans, history, show_deleted )
return trans.stream_template_mako( "root/history.mako",
history = history,
annotation = self.get_item_annotation_str( trans.sa_session, trans.get_user(), history ),
- datasets = query.all(),
+ datasets = datasets,
hda_id = hda_id,
show_deleted = show_deleted )
diff -r 8d9c8cf65dee -r fb48e02e1610 lib/galaxy/web/controllers/workflow.py
--- a/lib/galaxy/web/controllers/workflow.py Fri Feb 12 17:10:05 2010 -0500
+++ b/lib/galaxy/web/controllers/workflow.py Fri Feb 12 17:10:52 2010 -0500
@@ -78,7 +78,7 @@
# A public workflow is published, has a slug, and is not deleted.
return query.filter( self.model_class.published==True ).filter( self.model_class.slug != None ).filter( self.model_class.deleted == False )
-class WorkflowController( BaseController, Sharable ):
+class WorkflowController( BaseController, Sharable, UsesStoredWorkflow ):
stored_list_grid = StoredWorkflowListGrid()
published_list_grid = StoredWorkflowAllPublishedGrid()
@@ -93,7 +93,6 @@
status = message = None
if 'operation' in kwargs:
operation = kwargs['operation'].lower()
- print operation
if operation == "rename":
return self.rename( trans, **kwargs )
history_ids = util.listify( kwargs.get( 'id', [] ) )
@@ -165,7 +164,6 @@
@web.expose
def display_by_username_and_slug( self, trans, username, slug ):
""" Display workflow based on a username and slug. """
- session = trans.sa_session
# Get workflow.
session = trans.sa_session
@@ -181,34 +179,31 @@
raise web.httpexceptions.HTTPNotFound()
# Get data for workflow's steps.
- for step in stored_workflow.latest_workflow.steps:
- if step.type == 'tool' or step.type is None:
- # Restore the tool state for the step
- module = module_factory.from_workflow_step( trans, step )
- # Any connected input needs to have value DummyDataset (these
- # are not persisted so we need to do it every time)
- module.add_dummy_datasets( connections=step.input_connections )
- # Store state with the step
- step.module = module
- step.state = module.state
- # Error dict
- if step.tool_errors:
- errors[step.id] = step.tool_errors
- else:
- ## Non-tool specific stuff?
- step.module = module_factory.from_workflow_step( trans, step )
- step.state = step.module.get_runtime_state()
- # Connections by input name
- step.input_connections_by_name = dict( ( conn.input_name, conn ) for conn in step.input_connections )
+ self.get_stored_workflow_steps( trans, stored_workflow )
return trans.fill_template_mako( "workflow/display.mako", item=stored_workflow, item_data=stored_workflow.latest_workflow.steps )
+
+ @web.expose
+ @web.require_login("get item content asynchronously")
+ def get_item_content_async( self, trans, id ):
+ """ Returns item content in HTML format. """
+
+ stored = self.get_stored_workflow( trans, id, False, True )
+ if stored is None:
+ raise web.httpexceptions.HTTPNotFound()
+
+ # Get data for workflow's steps.
+ self.get_stored_workflow_steps( trans, stored )
+ # Get annotation.
+ annotation = self.get_item_annotation_str( trans.sa_session, trans.get_user(), stored )
+ return trans.stream_template_mako( "/workflow/item_content.mako", item = stored, item_data = stored.latest_workflow.steps )
@web.expose
@web.require_login( "use Galaxy workflows" )
def share( self, trans, id, email="" ):
msg = mtype = None
# Load workflow from database
- stored = get_stored_workflow( trans, id )
+ stored = self.get_stored_workflow( trans, id )
if email:
other = trans.sa_session.query( model.User ) \
.filter( and_( model.User.table.c.email==email,
@@ -247,7 +242,7 @@
# Get session and workflow.
session = trans.sa_session
- stored = get_stored_workflow( trans, id )
+ stored = self.get_stored_workflow( trans, id )
session.add( stored )
# Do operation on workflow.
@@ -285,7 +280,7 @@
@web.require_login( "use Galaxy workflows" )
def imp( self, trans, id, **kwargs ):
session = trans.sa_session
- stored = get_stored_workflow( trans, id, check_ownership=False )
+ stored = self.get_stored_workflow( trans, id, check_ownership=False )
if stored.importable == False:
error( "The owner of this workflow has disabled imports via this link" )
elif stored.user == trans.user:
@@ -309,7 +304,7 @@
@web.require_login( "use Galaxy workflows" )
def edit_attributes( self, trans, id, **kwargs ):
# Get workflow and do error checking.
- stored = get_stored_workflow( trans, id )
+ stored = self.get_stored_workflow( trans, id )
if not stored:
error( "You do not own this workflow or workflow ID is invalid." )
@@ -331,7 +326,7 @@
@web.expose
@web.require_login( "use Galaxy workflows" )
def rename( self, trans, id, new_name=None, **kwargs ):
- stored = get_stored_workflow( trans, id )
+ stored = self.get_stored_workflow( trans, id )
if new_name is not None:
stored.name = new_name
trans.sa_session.flush()
@@ -348,7 +343,7 @@
@web.expose
@web.require_login( "use Galaxy workflows" )
def rename_async( self, trans, id, new_name=None, **kwargs ):
- stored = get_stored_workflow( trans, id )
+ stored = self.get_stored_workflow( trans, id )
if new_name:
stored.name = new_name
trans.sa_session.flush()
@@ -357,7 +352,7 @@
@web.expose
@web.require_login( "use Galaxy workflows" )
def annotate_async( self, trans, id, new_annotation=None, **kwargs ):
- stored = get_stored_workflow( trans, id )
+ stored = self.get_stored_workflow( trans, id )
if new_annotation:
# Sanitize annotation before adding it.
new_annotation = sanitize_html( new_annotation, 'utf-8', 'text/html' )
@@ -369,7 +364,7 @@
@web.require_login( "use Galaxy workflows" )
def set_accessible_async( self, trans, id=None, accessible=False ):
""" Set workflow's importable attribute and slug. """
- stored = get_stored_workflow( trans, id )
+ stored = self.get_stored_workflow( trans, id )
# Only set if importable value would change; this prevents a change in the update_time unless attribute really changed.
importable = accessible in ['True', 'true', 't', 'T'];
@@ -385,18 +380,27 @@
@web.expose
@web.require_login( "modify Galaxy items" )
def set_slug_async( self, trans, id, new_slug ):
- stored = get_stored_workflow( trans, id )
+ stored = self.get_stored_workflow( trans, id )
if stored:
stored.slug = new_slug
trans.sa_session.flush()
return stored.slug
+
+ @web.expose
+ def get_embed_html_async( self, trans, id ):
+ """ Returns HTML for embedding a workflow in a page. """
+
+ # TODO: user should be able to embed any item he has access to. see display_by_username_and_slug for security code.
+ stored = self.get_stored_workflow( trans, id )
+ if stored:
+ return "Embedded Workflow '%s'" % stored.name
@web.expose
@web.json
@web.require_login( "use Galaxy workflows" )
def get_name_and_link_async( self, trans, id=None ):
""" Returns workflow's name and link. """
- stored = get_stored_workflow( trans, id )
+ stored = self.get_stored_workflow( trans, id )
if self.set_item_slug( trans.sa_session, stored ):
trans.sa_session.flush()
@@ -406,7 +410,7 @@
@web.expose
@web.require_login( "use Galaxy workflows" )
def clone( self, trans, id ):
- stored = get_stored_workflow( trans, id, check_ownership=False )
+ stored = self.get_stored_workflow( trans, id, check_ownership=False )
user = trans.get_user()
if stored.user == user:
owner = True
@@ -463,7 +467,7 @@
Mark a workflow as deleted
"""
# Load workflow from database
- stored = get_stored_workflow( trans, id )
+ stored = self.get_stored_workflow( trans, id )
# Marke as deleted and save
stored.deleted = True
trans.sa_session.add( stored )
@@ -482,7 +486,7 @@
"""
if not id:
error( "Invalid workflow id" )
- stored = get_stored_workflow( trans, id )
+ stored = self.get_stored_workflow( trans, id )
return trans.fill_template( "workflow/editor.mako", stored=stored, annotation=self.get_item_annotation_str( trans.sa_session, trans.get_user(), stored ) )
@web.json
@@ -611,7 +615,7 @@
Save the workflow described by `workflow_data` with id `id`.
"""
# Get the stored workflow
- stored = get_stored_workflow( trans, id )
+ stored = self.get_stored_workflow( trans, id )
# Put parameters in workflow mode
trans.workflow_building_mode = True
# Convert incoming workflow data from json
@@ -795,7 +799,7 @@
@web.expose
def run( self, trans, id, check_user=True, **kwargs ):
- stored = get_stored_workflow( trans, id, check_ownership=False )
+ stored = self.get_stored_workflow( trans, id, check_ownership=False )
if check_user:
user = trans.get_user()
if stored.user != user:
@@ -956,24 +960,6 @@
ids_in_menu=ids_in_menu )
## ---- Utility methods -------------------------------------------------------
-
-def get_stored_workflow( trans, id, check_ownership=True ):
- """
- Get a StoredWorkflow from the database by id, verifying ownership.
- """
- # Load workflow from database
- id = trans.security.decode_id( id )
- stored = trans.sa_session.query( model.StoredWorkflow ).get( id )
- if not stored:
- error( "Workflow not found" )
- # Verify ownership
- user = trans.get_user()
- if not user:
- error( "Must be logged in to use workflows" )
- if check_ownership and not( stored.user == user ):
- error( "Workflow is not owned by current user" )
- # Looks good
- return stored
def attach_ordered_steps( workflow, steps ):
ordered_steps = order_workflow_steps( steps )
diff -r 8d9c8cf65dee -r fb48e02e1610 scripts/galaxy_messaging/server/data_transfer.py
--- a/scripts/galaxy_messaging/server/data_transfer.py Fri Feb 12 17:10:05 2010 -0500
+++ b/scripts/galaxy_messaging/server/data_transfer.py Fri Feb 12 17:10:52 2010 -0500
@@ -38,9 +38,7 @@
pkg_resources.require( "simplejson" )
import simplejson
-curr_dir = os.getcwd()
-logfile = os.path.join(os.getcwd(), 'data_transfer.log')
-logging.basicConfig(filename=logfile, level=logging.DEBUG,
+logging.basicConfig(filename=sys.stderr, level=logging.DEBUG,
format="%(asctime)s [%(levelname)s] %(message)s")
class DataTransferException(Exception):
diff -r 8d9c8cf65dee -r fb48e02e1610 static/images/fugue/pencil.png
Binary file static/images/fugue/pencil.png has changed
diff -r 8d9c8cf65dee -r fb48e02e1610 static/images/fugue/sticky-note-text.png
Binary file static/images/fugue/sticky-note-text.png has changed
diff -r 8d9c8cf65dee -r fb48e02e1610 static/images/fugue/tag-label.png
Binary file static/images/fugue/tag-label.png has changed
diff -r 8d9c8cf65dee -r fb48e02e1610 static/images/fugue/toggle-expand.png
Binary file static/images/fugue/toggle-expand.png has changed
diff -r 8d9c8cf65dee -r fb48e02e1610 static/images/fugue/toggle.png
Binary file static/images/fugue/toggle.png has changed
diff -r 8d9c8cf65dee -r fb48e02e1610 static/images/pencil.png
Binary file static/images/pencil.png has changed
diff -r 8d9c8cf65dee -r fb48e02e1610 static/images/sticky-note-text.png
Binary file static/images/sticky-note-text.png has changed
diff -r 8d9c8cf65dee -r fb48e02e1610 static/images/tag-label.png
Binary file static/images/tag-label.png has changed
diff -r 8d9c8cf65dee -r fb48e02e1610 static/june_2007_style/base.css.tmpl
--- a/static/june_2007_style/base.css.tmpl Fri Feb 12 17:10:05 2010 -0500
+++ b/static/june_2007_style/base.css.tmpl Fri Feb 12 17:10:52 2010 -0500
@@ -703,7 +703,7 @@
}
.icon-button.tag {
- background-image: url(/static/images/tag-label.png);
+ background-image: url(/static/images/fugue/tag-label.png);
}
.icon-button.tags {
@@ -713,9 +713,17 @@
.icon-button.tag--plus {
background-image: url(/static/images/fugue/tag--plus.png);
}
+
+.icon-button.toggle-expand {
+ background-image:url(/static/images/fugue/toggle-expand.png);
+}
+
+.icon-button.toggle-contract {
+ background-image:url(/static/images/fugue/toggle.png);
+}
.icon-button.annotate {
- background-image:url(/static/images/sticky-note-text.png);
+ background-image:url(/static/images/fugue/sticky-note-text.png);
background-repeat:no-repeat;
background-position:center;
padding: 0;
diff -r 8d9c8cf65dee -r fb48e02e1610 static/june_2007_style/blue/base.css
--- a/static/june_2007_style/blue/base.css Fri Feb 12 17:10:05 2010 -0500
+++ b/static/june_2007_style/blue/base.css Fri Feb 12 17:10:52 2010 -0500
@@ -117,10 +117,12 @@
.icon-button.delete:hover{background:url(history-buttons.png) no-repeat 0px -78px;}
.icon-button.edit{background:url(history-buttons.png) no-repeat 0px -104px;}
.icon-button.edit:hover{background:url(history-buttons.png) no-repeat 0px -130px;}
-.icon-button.tag{background-image:url(/static/images/tag-label.png);}
+.icon-button.tag{background-image:url(/static/images/fugue/tag-label.png);}
.icon-button.tags{background-image:url(/static/images/fugue/tags.png);}
.icon-button.tag--plus{background-image:url(/static/images/fugue/tag--plus.png);}
-.icon-button.annotate{background-image:url(/static/images/sticky-note-text.png);background-repeat:no-repeat;background-position:center;padding:0;}
+.icon-button.toggle-expand{background-image:url(/static/images/fugue/toggle-expand.png);}
+.icon-button.toggle-contract{background-image:url(/static/images/fugue/toggle.png);}
+.icon-button.annotate{background-image:url(/static/images/fugue/sticky-note-text.png);background-repeat:no-repeat;background-position:center;padding:0;}
.tipsy{padding:5px;font-size:10px;filter:alpha(opacity=80);background-repeat:no-repeat;background-image:url(../images/tipsy.gif);}
.tipsy-inner{padding:5px 8px 4px 8px;background-color:black;color:white;max-width:200px;text-align:center;}
.tipsy-north{background-position:top center;}
diff -r 8d9c8cf65dee -r fb48e02e1610 static/june_2007_style/blue/embed_item.css
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/static/june_2007_style/blue/embed_item.css Fri Feb 12 17:10:52 2010 -0500
@@ -0,0 +1,4 @@
+.embedded-item{background-color: #BBBBBB; margin-left: auto; margin-right: auto; width: 90%; max-height: 25em; overflow: auto; padding: 0.5em;-moz-border-radius: .5em;-webkit-border-radius: .5em;border-radius: .5em;}
+.embedded-item.placeholder{}
+.embedded-item .title{font-weight: bold;font-size:120%;vertical-align:top;text-align:center;}
+.embedded-item.placeholder .content{padding: 1em 1em;font-style:italic;text-align:center;}
\ No newline at end of file
diff -r 8d9c8cf65dee -r fb48e02e1610 static/june_2007_style/embed_item.css.tmpl
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/static/june_2007_style/embed_item.css.tmpl Fri Feb 12 17:10:52 2010 -0500
@@ -0,0 +1,24 @@
+.embedded-item {
+ background-color: #BBBBBB;
+ margin-left: auto;
+ margin-right: auto;
+ width: 90%;
+ max-height: 25em;
+ overflow: auto;
+ padding: 0.5em;
+ -moz-border-radius: .5em;
+ -webkit-border-radius: .5em;
+ border-radius: .5em;
+}
+.embedded-item.placeholder {}
+.embedded-item .title {
+ font-weight: bold;
+ font-size:120%;
+ vertical-align:top;
+ text-align:center;
+}
+.embedded-item.placeholder .content{
+ padding: 1em 1em;
+ font-style:italic;
+ text-align:center;
+ }
\ No newline at end of file
diff -r 8d9c8cf65dee -r fb48e02e1610 templates/dataset/display.mako
--- a/templates/dataset/display.mako Fri Feb 12 17:10:05 2010 -0500
+++ b/templates/dataset/display.mako Fri Feb 12 17:10:52 2010 -0500
@@ -27,7 +27,6 @@
</%def>
<%def name="render_item( data, data_to_render )">
- <hr/>
%if truncated:
<div class="warningmessagelarge">
This dataset is large and only the first megabyte is shown below. |
@@ -82,7 +81,7 @@
## Tags.
<p>
- <h4>Tags</strong></h4>
+ <h4>Tags</h4>
<p>
## Community tags.
<div>
diff -r 8d9c8cf65dee -r fb48e02e1610 templates/dataset/embed.mako
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/dataset/embed.mako Fri Feb 12 17:10:52 2010 -0500
@@ -0,0 +1,13 @@
+<%inherit file="/embed_base.mako"/>
+<%!
+ from galaxy.web.framework.helpers import iff
+%>
+
+<%def name="content( dataset, data )">
+ %if annotation:
+ <div class='annotation'>${annotation}</div>
+ %endif
+ <ul>
+ <li>Format : ${dataset.extension}
+ </ul>
+</%def>
diff -r 8d9c8cf65dee -r fb48e02e1610 templates/dataset/item_content.mako
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/dataset/item_content.mako Fri Feb 12 17:10:52 2010 -0500
@@ -0,0 +1,3 @@
+<%namespace file="/dataset/display.mako" import="*" />
+
+${render_item( item, item_data )}
\ No newline at end of file
diff -r 8d9c8cf65dee -r fb48e02e1610 templates/display_base.mako
--- a/templates/display_base.mako Fri Feb 12 17:10:05 2010 -0500
+++ b/templates/display_base.mako Fri Feb 12 17:10:52 2010 -0500
@@ -12,6 +12,10 @@
<%namespace file="/tagging_common.mako" import="render_individual_tagging_element, render_community_tagging_element" />
<%namespace file="/display_common.mako" import="*" />
+##
+## Functions used by base.mako and base_panels.mako to display content.
+##
+
<%def name="title()">
Galaxy | ${iff( item.published, "Published ", iff( item.importable , "Accessible ", iff( item.users_shared_with, "Shared ", "Private " ) ) ) + get_class_display_name( item.__class__ )} | ${get_item_name( item ) | h}
</%def>
@@ -48,7 +52,7 @@
<%def name="stylesheets()">
${parent.stylesheets()}
- ${h.css( "autocomplete_tagging" )}
+ ${h.css( "autocomplete_tagging", "embed_item" )}
<style type="text/css">
.page-body
{
@@ -70,24 +74,28 @@
</%def>
<%def name="render_item_links( item )">
- Item Links
+ ## Override.
</%def>
<%def name="render_item( item, item_data=None )">
- Item
+ ## Override.
</%def>
-##
-## When page has no panels, center panel is body.
-##
+## For base.mako
<%def name="body()">
- ${self.center_panel()}
+ ${self.render_content()}
</%def>
+## For base_panels.mako
+<%def name="center_panel()">
+ ${self.render_content()}
+</%def>
+
+
##
-## Page content. Pages that inherit this page should override render_item_links() and render_item()
+## Render page content. Pages that inherit this page should override render_item_links() and render_item()
##
-<%def name="center_panel()">
+<%def name="render_content()">
## Get URL to other published items owned by user that owns this item.
<%
@@ -165,7 +173,7 @@
<a href="${href_to_user_items}">Published ${item_plural.lower()} by ${item.user.username | h}</a>
## Tags.
- <h4>Tags</strong></h4>
+ <h4>Tags</h4>
<p>
## Community tags.
<div>
diff -r 8d9c8cf65dee -r fb48e02e1610 templates/display_common.mako
--- a/templates/display_common.mako Fri Feb 12 17:10:05 2010 -0500
+++ b/templates/display_common.mako Fri Feb 12 17:10:52 2010 -0500
@@ -83,11 +83,35 @@
return "history"
elif isinstance( item, model.StoredWorkflow ):
return "workflow"
+ elif isinstance( item, model.HistoryDatasetAssociation ):
+ return "dataset"
elif isinstance( item, model.Page ):
return "page"
%>
</%def>
+## Returns item user/owner.
+<%def name="get_item_user( item )">
+ <%
+ # Exceptions first, default last.
+ if isinstance( item, model.HistoryDatasetAssociation ):
+ return item.history.user
+ else:
+ return item.user
+ %>
+</%def>
+
+## Returns item slug.
+<%def name="get_item_slug( item )">
+ <%
+ # Exceptions first, default last.
+ if isinstance( item, model.HistoryDatasetAssociation ):
+ return trans.security.encode_id( item.id )
+ else:
+ return item.slug
+ %>
+</%def>
+
## Return a link to view a history.
<%def name="get_history_link( history, qualify=False )">
%if history.slug and history.user.username:
diff -r 8d9c8cf65dee -r fb48e02e1610 templates/embed_base.mako
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/embed_base.mako Fri Feb 12 17:10:52 2010 -0500
@@ -0,0 +1,38 @@
+##
+## Base file for generating HTML for embedded objects.
+##
+## parameters: item, item_data
+##
+<%namespace file="/display_common.mako" import="*" />
+
+## HTML structure.
+<div class='embedded-item'>
+ <div class='title'>
+ ${self.title( item )}
+ </div>
+ <hr/>
+ <div class='summary-content'>
+ ${self.content( item, item_data )}
+ </div>
+ <div class='item-content'>
+ </div>
+</div>
+
+<%def name="title( item )">
+ Galaxy ${get_class_display_name( item.__class__ )} | ${get_item_name( item )}
+ <%
+ item_controller = "/%s" % get_controller_name( item )
+ item_user = get_item_user( item )
+ item_slug = get_item_slug( item )
+ display_href = h.url_for( controller=item_controller, action='display_by_username_and_slug', username=item_user.username, slug=item_slug )
+ %>
+ <a class="display_in_embed icon-button toggle-expand" item_id="${trans.security.encode_id( item.id )}" item_class="$item.__class__.__name__" href="${display_href}"></a>
+ <a class="toggle-contract icon-button" href="${display_href}"></a>
+
+ ## Use a hidden var to store the ajax URL for getting an item's content.
+ <input type="hidden" name="ajax-item-content-url" value="${h.url_for( controller=item_controller, action='get_item_content_async', id=trans.security.encode_id( item.id ) )}"/>
+</%def>
+
+## Methods to override to generate content.
+<%def name="content( item, item_data )">
+</%def>
diff -r 8d9c8cf65dee -r fb48e02e1610 templates/history/embed.mako
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/history/embed.mako Fri Feb 12 17:10:52 2010 -0500
@@ -0,0 +1,15 @@
+<%inherit file="/embed_base.mako"/>
+<%!
+ from galaxy.web.framework.helpers import iff
+%>
+
+<%def name="content( history, datasets )">
+ %if annotation:
+ <div class='annotation'>${annotation}</div>
+ %endif
+ <ul>
+ <% num_datasets = len ( datasets ) %>
+ <li>${num_datasets} dataset${iff( num_datasets != 1, "s", "" )}
+ <li>Operations: ...
+ </ul>
+</%def>
diff -r 8d9c8cf65dee -r fb48e02e1610 templates/history/item_content.mako
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/history/item_content.mako Fri Feb 12 17:10:52 2010 -0500
@@ -0,0 +1,3 @@
+<%namespace file="/history/display.mako" import="*" />
+
+${render_item( item, item_data )}
\ No newline at end of file
diff -r 8d9c8cf65dee -r fb48e02e1610 templates/page/display.mako
--- a/templates/page/display.mako Fri Feb 12 17:10:05 2010 -0500
+++ b/templates/page/display.mako Fri Feb 12 17:10:52 2010 -0500
@@ -25,6 +25,54 @@
});
});
+
+ // Setup embedded content:
+ // (a) toggles for showing/hiding embedded content;
+ // (b) ...
+ $('.embedded-item').each( function()
+ {
+ // Setup toggle expand.
+ var container = $(this);
+ var toggle_expand = $(this).find('.toggle-expand');
+ toggle_expand.click( function()
+ {
+ var ajax_url = container.find("input[type=hidden]").val();
+ // Only get item content if it's not already there.
+ var item_content = $.trim(container.find(".item-content").text());
+ if (item_content == "")
+ $.ajax({
+ type: "GET",
+ url: ajax_url,
+ error: function() { alert("Getting item content failed."); },
+ success: function( item_content ) {
+ container.find(".summary-content").hide("fast");
+ container.find(".item-content").html(item_content).show("fast");
+ container.find(".toggle-expand").hide();
+ container.find(".toggle-contract").show();
+ }
+ });
+ else
+ {
+ container.find(".summary-content").hide("fast");
+ container.find(".item-content").show("fast");
+ container.find(".toggle-expand").hide();
+ container.find(".toggle-contract").show();
+ }
+ return false;
+ });
+ // Setup toggle contract.
+ var toggle_contract = $(this).find('.toggle-contract');
+ toggle_contract.click( function()
+ {
+ container.find(".item-content").hide("fast");
+ container.find(".summary-content").show("fast");
+ container.find(".toggle-contract").hide();
+ container.find(".toggle-expand").show();
+ return false;
+ });
+
+
+ });
});
// Functionized so AJAX'd datasets can call them
function initShowHide() {
@@ -148,15 +196,15 @@
<%def name="stylesheets()">
${parent.stylesheets()}
${h.css( "base", "history", "autocomplete_tagging" )}
-</%def>
-
-<%def name="get_item_name( page )">
- <% return page.title %>
+ <style type="text/css">
+ .toggle-contract { display: none; }
+ .item-content { overflow: auto; }
+ </style>
</%def>
<%def name="render_item_links( page )">
</%def>
<%def name="render_item( page, page_data=None )">
- ${page.latest_revision.content.decode( "utf-8" )}
+ ${page_data}
</%def>
\ No newline at end of file
diff -r 8d9c8cf65dee -r fb48e02e1610 templates/page/editor.mako
--- a/templates/page/editor.mako Fri Feb 12 17:10:05 2010 -0500
+++ b/templates/page/editor.mako Fri Feb 12 17:10:52 2010 -0500
@@ -29,10 +29,25 @@
// Useful Galaxy stuff.
var Galaxy =
{
- DIALOG_HISTORY_LINK : "history_link",
- DIALOG_DATASET_LINK : "dataset_link",
- DIALOG_WORKFLOW_LINK : "workflow_link",
- DIALOG_PAGE_LINK : "page_link",
+ // Item types.
+ ITEM_HISTORY : "item_history",
+ ITEM_DATASET : "item_dataset",
+ ITEM_WORKFLOW : "item_workflow",
+ ITEM_PAGE : "item_page",
+
+ // Link dialogs.
+ DIALOG_HISTORY_LINK : "link_history",
+ DIALOG_DATASET_LINK : "link_dataset",
+ DIALOG_WORKFLOW_LINK : "link_workflow",
+ DIALOG_PAGE_LINK : "link_page",
+
+ // Embed dialogs.
+ DIALOG_EMBED_HISTORY : "embed_history",
+ DIALOG_EMBED_DATASET : "embed_dataset",
+ DIALOG_EMBED_WORKFLOW : "embed_workflow",
+ DIALOG_EMBED_PAGE : "embed_page",
+
+ // Annotation dialogs.
DIALOG_HISTORY_ANNOTATE : "history_annotate",
};
@@ -55,6 +70,55 @@
};
+ // Based on the dialog type, return a dictionary of information about an item
+ function get_item_info( dialog_type )
+ {
+ var
+ item_singular,
+ item_plural,
+ item_controller;
+ switch( dialog_type ) {
+ case( Galaxy.ITEM_HISTORY ):
+ item_singular = "History";
+ item_plural = "Histories";
+ item_controller = "history";
+ item_class = "History";
+ break;
+ case( Galaxy.ITEM_DATASET ):
+ item_singular = "Dataset";
+ item_plural = "Datasets";
+ item_controller = "dataset";
+ item_class = "HistoryDatasetAssociation";
+ break;
+ case( Galaxy.ITEM_WORKFLOW ):
+ item_singular = "Workflow";
+ item_plural = "Workflows";
+ item_controller = "workflow";
+ item_class = "StoredWorkflow";
+ break;
+ case( Galaxy.ITEM_PAGE ):
+ item_singular = "Page";
+ item_plural = "Pages";
+ item_controller = "page";
+ item_class = "Page";
+ break;
+ }
+
+ // Build ajax URL that lists items for selection.
+ var item_list_action = "list_" + item_plural.toLowerCase() + "_for_selection";
+ var url_template = "${h.url_for( action='LIST_ACTION' )}";
+ var ajax_url = url_template.replace( "LIST_ACTION", item_list_action );
+
+ // Set up and return dict.
+ return {
+ singular : item_singular,
+ plural : item_plural,
+ controller : item_controller,
+ iclass : item_class,
+ list_ajax_url : ajax_url
+ };
+ };
+
## Completely replace WYM's dialog handling
WYMeditor.editor.prototype.dialog = function( dialogType, dialogFeatures, bodyHtml ) {
@@ -206,50 +270,35 @@
if ( dialogType == Galaxy.DIALOG_HISTORY_LINK || dialogType == Galaxy.DIALOG_DATASET_LINK ||
dialogType == Galaxy.DIALOG_WORKFLOW_LINK || dialogType == Galaxy.DIALOG_PAGE_LINK ) {
// Based on item type, set useful vars.
- var
- item_singular,
- item_plural,
- item_controller,
- item_list_action;
- switch( dialogType ) {
- case( Galaxy.DIALOG_HISTORY_LINK ):
- item_singular = "History";
- item_plural = "Histories";
- item_controller = "history"
+ var item_info;
+ switch(dialogType)
+ {
+ case(Galaxy.DIALOG_HISTORY_LINK):
+ item_info = get_item_info(Galaxy.ITEM_HISTORY);
break;
- case( Galaxy.DIALOG_DATASET_LINK ):
- item_singular = "Dataset";
- item_plural = "Datasets";
- item_controller = "dataset"
+ case(Galaxy.DIALOG_DATASET_LINK):
+ item_info = get_item_info(Galaxy.ITEM_DATASET);
break;
- case( Galaxy.DIALOG_WORKFLOW_LINK ):
- item_singular = "Workflow";
- item_plural = "Workflows";
- item_controller = "workflow"
+ case(Galaxy.DIALOG_WORKFLOW_LINK):
+ item_info = get_item_info(Galaxy.ITEM_WORKFLOW);
break;
- case( Galaxy.DIALOG_PAGE_LINK ):
- item_singular = "Page";
- item_plural = "Pages";
- item_controller = "page"
- break;
+ case(Galaxy.DIALOG_PAGE_LINK):
+ item_info = get_item_info(Galaxy.ITEM_PAGE);
+ break;
}
- item_list_action = "list_" + item_plural.toLowerCase() + "_for_selection";
- // Show grid that enables user to select items.
- var url_template = "${h.url_for( action='LIST_ACTION' )}";
- var ajax_url = url_template.replace( "LIST_ACTION", item_list_action );
$.ajax(
{
- url: ajax_url,
+ url: item_info.list_ajax_url,
data: {},
- error: function() { alert( "Failed to list " + item_plural.toLowerCase() + " for selection"); },
+ error: function() { alert( "Failed to list " + item_info.plural.toLowerCase() + " for selection"); },
success: function(table_html)
{
show_modal(
- "Insert Link to " + item_singular,
+ "Insert Link to " + item_info.singular,
table_html +
"<div><input id='make-importable' type='checkbox' checked/>" +
- "Make the selected " + item_plural.toLowerCase() + " accessible so that they can viewed by everyone.</div>"
+ "Make the selected " + item_info.plural.toLowerCase() + " accessible so that they can viewed by everyone.</div>"
,
{
"Insert": function()
@@ -264,23 +313,23 @@
$('input[name=id]:checked').each(function() {
var item_id = $(this).val();
- // Make history importable?
+ // Make item importable?
if (make_importable)
{
url_template = "${h.url_for( controller='ITEM_CONTROLLER', action='set_accessible_async' )}";
- ajax_url = url_template.replace( "ITEM_CONTROLLER", item_controller);
+ ajax_url = url_template.replace( "ITEM_CONTROLLER", item_info.controller);
$.ajax({
type: "POST",
url: ajax_url,
data: { id: item_id, accessible: 'True' },
- error: function() { alert("Making " + item_plural.toLowerCase() + " accessible failed"); }
+ error: function() { alert("Making " + item_info.plural.toLowerCase() + " accessible failed"); }
});
}
// Insert link(s) to item(s). This is done by getting item info and then manipulating wym.
url_template = "${h.url_for( controller='ITEM_CONTROLLER', action='get_name_and_link_async' )}?id=" + item_id;
- ajax_url = url_template.replace( "ITEM_CONTROLLER", item_controller);
- $.getJSON( ajax_url, function( item_info ) {
+ ajax_url = url_template.replace( "ITEM_CONTROLLER", item_info.controller);
+ $.getJSON( ajax_url, function( returned_item_info ) {
// Get link text.
wym._exec(WYMeditor.CREATE_LINK, sStamp);
var link_text = $("a[href=" + sStamp + "]", wym._doc.body).text();
@@ -293,12 +342,12 @@
)
{
// User selected no text; create link from scratch and use default text.
- wym.insert("<a href='" + item_info.link + "'> '" + item_singular + " " + item_info.name + "'</a>");
+ wym.insert("<a href='" + returned_item_info.link + "'>" + item_info.singular + " '" + returned_item_info.name + "'</a>");
}
else
{
// Link created from selected text; add href and title.
- $("a[href=" + sStamp + "]", wym._doc.body).attr(WYMeditor.HREF, item_info.link).attr(WYMeditor.TITLE, item_singular + item_id);
+ $("a[href=" + sStamp + "]", wym._doc.body).attr(WYMeditor.HREF, returned_item_info.link).attr(WYMeditor.TITLE, item_info.singular + item_id);
}
});
});
@@ -314,6 +363,72 @@
}
});
}
+ // EMBED GALAXY OBJECT DIALOGS
+ if ( dialogType == Galaxy.DIALOG_EMBED_HISTORY || dialogType == Galaxy.DIALOG_EMBED_DATASET || dialogType == Galaxy.DIALOG_EMBED_WORKFLOW || dialogType == Galaxy.DIALOG_EMBED_PAGE ) {
+ // Based on item type, set useful vars.
+ var item_info;
+ switch(dialogType)
+ {
+ case(Galaxy.DIALOG_EMBED_HISTORY):
+ item_info = get_item_info(Galaxy.ITEM_HISTORY);
+ break;
+ case(Galaxy.DIALOG_EMBED_DATASET):
+ item_info = get_item_info(Galaxy.ITEM_DATASET);
+ break;
+ case(Galaxy.DIALOG_EMBED_WORKFLOW):
+ item_info = get_item_info(Galaxy.ITEM_WORKFLOW);
+ break;
+ case(Galaxy.DIALOG_EMBED_PAGE):
+ item_info = get_item_info(Galaxy.ITEM_PAGE);
+ break;
+ }
+
+ $.ajax(
+ {
+ url: item_info.list_ajax_url,
+ data: {},
+ error: function() { alert( "Failed to list " + item_info.plural.toLowerCase() + " for selection"); },
+ success: function(list_html)
+ {
+ show_modal(
+ "Embed " + item_info.plural,
+ list_html,
+ {
+ "Embed": function()
+ {
+ // Embed a Galaxy item.
+ var item_ids = new Array();
+ $('input[name=id]:checked').each(function() {
+ // Get item ID and name.
+ var item_id = $(this).val();
+ // Use ':first' because there are many labels in table; the first one is the item name.
+ var item_name = $("label[for='" + item_id + "']:first").text();
+
+ // Embedded item HTML; item class is embedded in div container classes; this is necessary because the editor strips
+ // all non-standard attributes when it returns its content (e.g. it will not return an element attribute of the form
+ // item_class='History').
+ var item_embed_html =
+ "<p><div id='" + item_info.iclass + "-" + item_id + "' class='embedded-item placeholder'> \
+ <div class='title'> Embedded Galaxy " + item_info.singular + " '" + item_name + "'</div> \
+ <div class='content'>[Do not edit this block; Galaxy will fill it in with the annotated " + \
+ item_info.singular.toLowerCase() + " when it is displayed.]</div> \
+ </div></p><p>";
+
+ // Insert embedded representation into document.
+ wym.insert(item_embed_html);
+ });
+ hide_modal();
+ },
+ "Cancel": function()
+ {
+ hide_modal();
+ }
+ }
+ );
+ }
+ });
+ }
+
// ANNOTATE HISTORY DIALOG
if ( dialogType == Galaxy.DIALOG_ANNOTATE_HISTORY ) {
$.ajax(
@@ -418,6 +533,9 @@
var editor = $.wymeditors(0);
var save = function ( callback ) {
show_modal( "Saving page", "progress" );
+
+ /*
+ Not used right now.
// Gather annotations.
var annotations = new Array();
@@ -433,6 +551,8 @@
annotations[ annotations.length ] = annotation;
});
+ */
+
// Do save.
$.ajax( {
url: "${h.url_for( action='save' )}",
@@ -440,7 +560,8 @@
data: {
id: "${trans.security.encode_id(page.id)}",
content: editor.xhtml(),
- annotations: JSON.stringify(annotations),
+ annotations: JSON.stringify(new Object()),
+ ## annotations: JSON.stringify(annotations),
"_": "true"
},
success: function() {
@@ -500,13 +621,69 @@
});
// Initialize galaxy elements.
//init_galaxy_elts(editor);
+
+ //
+ // Create 'Insert Link to Galaxy Object' menu.
+ //
+
+ // Add menu button.
+ var insert_link_menu_button = $("<div><a id='insert-galaxy-link' class='panel-header-button popup' href='#'>${_('Insert Link to Galaxy Object')}</a></div>").addClass('galaxy-page-editor-button');
+ $(".wym_area_top").append(insert_link_menu_button);
+
+ // Add menu options.
+ make_popupmenu( insert_link_menu_button, {
+ "Insert History Link": function() {
+ editor.dialog(Galaxy.DIALOG_HISTORY_LINK);
+ },
+ "Insert Dataset Link": function() {
+ editor.dialog(Galaxy.DIALOG_DATASET_LINK);
+ },
+ "Insert Workflow Link": function() {
+ editor.dialog(Galaxy.DIALOG_WORKFLOW_LINK);
+ },
+ "Insert Page Link": function() {
+ editor.dialog(Galaxy.DIALOG_PAGE_LINK);
+ }
+ });
+
+ //
+ // Create 'Embed Galaxy Object' menu.
+ //
+
+ // Add menu button.
+ var embed_object_button = $("<div><a id='embed-galaxy-object' class='panel-header-button popup' href='#'>${_('Embed Galaxy Object')}</a></div>").addClass('galaxy-page-editor-button');
+ $(".wym_area_top").append(embed_object_button);
+
+ // Add menu options.
+ make_popupmenu( embed_object_button, {
+ "Embed History": function() {
+ editor.dialog(Galaxy.DIALOG_EMBED_HISTORY);
+ },
+ "Embed Dataset": function() {
+ editor.dialog(Galaxy.DIALOG_EMBED_DATASET);
+ },
+ "Embed Workflow": function() {
+ editor.dialog(Galaxy.DIALOG_EMBED_WORKFLOW);
+ },
+ ##"Embed Page": function() {
+ ## editor.dialog(Galaxy.DIALOG_EMBED_PAGE);
+ ##}
+ });
});
</script>
</%def>
<%def name="stylesheets()">
${parent.stylesheets()}
- ${h.css( "base", "history", "autocomplete_tagging" )}
+ ${h.css( "base", "autocomplete_tagging", "embed_item" )}
+ <style type='text/css'>
+ .galaxy-page-editor-button
+ {
+ position: relative;
+ float: left;
+ padding: 0.2em;
+ }
+ </style>
</%def>
<%def name="center_panel()">
diff -r 8d9c8cf65dee -r fb48e02e1610 templates/page/wymiframe.mako
--- a/templates/page/wymiframe.mako Fri Feb 12 17:10:05 2010 -0500
+++ b/templates/page/wymiframe.mako Fri Feb 12 17:10:52 2010 -0500
@@ -21,7 +21,7 @@
<title>WYMeditor iframe</title>
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
<link rel="stylesheet" type="text/css" media="screen" href="/static/wymeditor/iframe/galaxy/wymiframe.css" />
- ${h.css("base", "history", "autocomplete_tagging")}
+ ${h.css("base", "autocomplete_tagging", "embed_item")}
</head>
<body class="wym_iframe text-content"></body>
</html>
diff -r 8d9c8cf65dee -r fb48e02e1610 templates/sharing_base.mako
--- a/templates/sharing_base.mako Fri Feb 12 17:10:05 2010 -0500
+++ b/templates/sharing_base.mako Fri Feb 12 17:10:52 2010 -0500
@@ -127,7 +127,7 @@
${"/".join( url_parts[:-1] )}/<span id='item-identifier'>${url_parts[-1]}</span>
</span>
- <a href="#" id="edit-identifier"><img src="${h.url_for('/static/images/pencil.png')}"/></a>
+ <a href="#" id="edit-identifier"><img src="${h.url_for('/static/images/fugue/pencil.png')}"/></a>
</blockquote>
%if item.published:
diff -r 8d9c8cf65dee -r fb48e02e1610 templates/workflow/display.mako
--- a/templates/workflow/display.mako Fri Feb 12 17:10:05 2010 -0500
+++ b/templates/workflow/display.mako Fri Feb 12 17:10:52 2010 -0500
@@ -58,7 +58,6 @@
other_values = {}
value = other_values[ param.name ] = param.get_initial_value( t, other_values )
%>
- <% print param.__class__ %>
${param.get_html_field( t, value, other_values ).get_html( str(step.id) + "|" + prefix )}
%endif
%else:
diff -r 8d9c8cf65dee -r fb48e02e1610 templates/workflow/embed.mako
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/workflow/embed.mako Fri Feb 12 17:10:52 2010 -0500
@@ -0,0 +1,15 @@
+<%inherit file="/embed_base.mako"/>
+<%!
+ from galaxy.web.framework.helpers import iff
+%>
+
+<%def name="content( workflow, steps )">
+ %if annotation:
+ <div class='annotation'>${annotation}</div>
+ %endif
+ <ul>
+ <% num_steps = len ( steps ) %>
+ <li>${num_steps} step${iff( num_steps != 1, "s", "" )}
+ <li>Operations: ...
+ </ul>
+</%def>
diff -r 8d9c8cf65dee -r fb48e02e1610 templates/workflow/item_content.mako
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/workflow/item_content.mako Fri Feb 12 17:10:52 2010 -0500
@@ -0,0 +1,3 @@
+<%namespace file="/workflow/display.mako" import="*" />
+
+${render_item( item, item_data )}
\ No newline at end of file
1
0