From: Brandon King Date: Wed, 24 Jun 2009 21:51:59 +0000 (+0000) Subject: Sample Tracker EXTJS frontend merge part 1 X-Git-Tag: 0.2.6~38 X-Git-Url: http://woldlab.caltech.edu/gitweb/?p=htsworkflow.git;a=commitdiff_plain;h=af103f0454332b3f9cb7b2988b68c42da7f0314a Sample Tracker EXTJS frontend merge part 1 --- diff --git a/htsworkflow/frontend/samples/views.py b/htsworkflow/frontend/samples/views.py index abc4c6e..c72a833 100644 --- a/htsworkflow/frontend/samples/views.py +++ b/htsworkflow/frontend/samples/views.py @@ -14,12 +14,16 @@ from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import render_to_response from django.template import RequestContext from django.template.loader import get_template +from django.contrib.auth.decorators import login_required import StringIO import logging import os LANE_LIST = [1,2,3,4,5,6,7,8] +SAMPLES_CONTEXT_DEFAULTS = { + 'app_name': 'Flowcell/Library Tracker' +} def create_library_context(cl): """ @@ -59,7 +63,16 @@ def library(request): context.update(create_library_context(fcl)) t = get_template('samples/library_index.html') c = RequestContext(request, context) - return HttpResponse( t.render(c) ) + + app_context = { + 'page_name': 'Library Index', + 'body': t.render(c) + } + app_context.update(SAMPLES_CONTEXT_DEFAULTS) + + app_t = get_template('flowcell_libraries_app.html') + app_c = RequestContext(request, app_context) + return HttpResponse( app_t.render(app_c) ) def library_to_flowcells(request, lib_id): """ @@ -411,3 +424,17 @@ def library_id_to_admin_url(request, lib_id): lib = Library.objects.get(library_id=lib_id) return HttpResponseRedirect('/admin/samples/library/%s' % (lib.id,)) +@login_required +def user_profile(request): + """ + Information about the user + """ + context = { + 'page_name': 'User Profile', + 'media': '', + #'bcmagic': BarcodeMagicForm(), + #'select': 'settings', + } + context.update(SAMPLES_CONTEXT_DEFAULTS) + return render_to_response('registration/profile.html', context, + context_instance=RequestContext(request)) \ No newline at end of file diff --git a/htsworkflow/frontend/static/css/app.css b/htsworkflow/frontend/static/css/app.css new file mode 100644 index 0000000..49e3965 --- /dev/null +++ b/htsworkflow/frontend/static/css/app.css @@ -0,0 +1,35 @@ +#header { + background: #7F99BE url(/static/img/layout-browser-hd-bg.gif) repeat-x center; +} +#header h1 { + font-size: 16px; + color: #fff; + font-weight: normal; + padding: 5px 10px; +} + +#left_bar { + position: absolute; + top: 5px; + left: 5px; + width: 172px; + height: auto; + min-height: 600px; + + +} + +#main { + position: relative; + /* background: #BBBBFF; */ + width:auto; + min-width:120px; + padding: 10px; + margin:0px 0px 0px 175px; + min-height:600px; +} + +div.msg { + color: white; + background: #880000; +} \ No newline at end of file diff --git a/htsworkflow/frontend/static/css/click-table.css b/htsworkflow/frontend/static/css/click-table.css index 1d48412..34a8594 100644 --- a/htsworkflow/frontend/static/css/click-table.css +++ b/htsworkflow/frontend/static/css/click-table.css @@ -1,19 +1,19 @@ -table, td { +table.libtable, td.libtable { border-style: solid; } -table { +table.libtable { border-width: 0 0 1px 1px; border-spacing: 0; border-collapse: collapse; } -thead { +thead.libtable { text-align: center; } -td { +td.libtable { margin: 0; padding: 4px; border-width: 1px 1px 0 0; } -td a { +td.libtable a { display: block; } diff --git a/htsworkflow/frontend/static/css/global.css b/htsworkflow/frontend/static/css/global.css index fd84060..7aebac5 100644 --- a/htsworkflow/frontend/static/css/global.css +++ b/htsworkflow/frontend/static/css/global.css @@ -54,15 +54,15 @@ p img, h1 img, h2 img, h3 img, h4 img, td img { vertical-align:middle; } .nowrap { white-space:nowrap; } /* TABLES */ -table { border-collapse:collapse; border-color:#ccc; } -td, th { font-size:11px; line-height:13px; border-bottom:1px solid #eee; vertical-align:top; padding:5px; font-family:"Lucida Grande", Verdana, Arial, sans-serif; } -th { text-align:left; font-size:12px; font-weight:bold; } -thead th, -tfoot td { color:#666; padding:2px 5px; font-size:11px; background:#e1e1e1 url(../img/nav-bg.gif) top left repeat-x; border-left:1px solid #ddd; border-bottom:1px solid #ddd; } -tfoot td { border-bottom:none; border-top:1px solid #ddd; } -thead th:first-child, -tfoot td:first-child { border-left:none !important; } -thead th.optional { font-weight:normal !important; } +table.libtable { border-collapse:collapse; border-color:#ccc; } +td.libtable, th.libtable { font-size:11px; line-height:13px; border-bottom:1px solid #eee; vertical-align:top; padding:5px; font-family:"Lucida Grande", Verdana, Arial, sans-serif; } +th.libtable { text-align:left; font-size:12px; font-weight:bold; } +thead.libtable th, +tfoot.libtable td { color:#666; padding:2px 5px; font-size:11px; background:#e1e1e1 url(../img/nav-bg.gif) top left repeat-x; border-left:1px solid #ddd; border-bottom:1px solid #ddd; } +tfoot.libtable td { border-bottom:none; border-top:1px solid #ddd; } +thead.libtable th:first-child, +tfoot.libtable td:first-child { border-left:none !important; } +thead.libtable th.optional { font-weight:normal !important; } fieldset table { border-right:1px solid #eee; } tr.row-label td { font-size:9px; padding-top:2px; padding-bottom:0; border-bottom:none; color:#666; margin-top:-1px; } tr.alt { background:#f6f6f6; } @@ -70,11 +70,11 @@ tr.alt { background:#f6f6f6; } .row2 { background:white; } /* SORTABLE TABLES */ -thead th a:link, thead th a:visited { color:#666; display:block; } -table thead th.sorted { background-position:bottom left !important; } -table thead th.sorted a { padding-right:13px; } -table thead th.ascending a { background:url(../img/arrow-down.gif) right .4em no-repeat; } -table thead th.descending a { background:url(../img/arrow-up.gif) right .4em no-repeat; } +thead.libtable th a:link, thead th a:visited { color:#666; display:block; } +table.libtable thead th.sorted { background-position:bottom left !important; } +table.libtable thead th.sorted a { padding-right:13px; } +table.libtable thead th.ascending a { background:url(../img/arrow-down.gif) right .4em no-repeat; } +table.libtable thead th.descending a { background:url(../img/arrow-up.gif) right .4em no-repeat; } /* ORDERABLE TABLES */ table.orderable tbody tr td:hover { cursor:move; } diff --git a/htsworkflow/frontend/static/css/htsw.css b/htsworkflow/frontend/static/css/htsw.css new file mode 100644 index 0000000..49e3965 --- /dev/null +++ b/htsworkflow/frontend/static/css/htsw.css @@ -0,0 +1,35 @@ +#header { + background: #7F99BE url(/static/img/layout-browser-hd-bg.gif) repeat-x center; +} +#header h1 { + font-size: 16px; + color: #fff; + font-weight: normal; + padding: 5px 10px; +} + +#left_bar { + position: absolute; + top: 5px; + left: 5px; + width: 172px; + height: auto; + min-height: 600px; + + +} + +#main { + position: relative; + /* background: #BBBBFF; */ + width:auto; + min-width:120px; + padding: 10px; + margin:0px 0px 0px 175px; + min-height:600px; +} + +div.msg { + color: white; + background: #880000; +} \ No newline at end of file diff --git a/htsworkflow/frontend/static/img/layout-browser-hd-bg.gif b/htsworkflow/frontend/static/img/layout-browser-hd-bg.gif new file mode 100644 index 0000000..29bf23f Binary files /dev/null and b/htsworkflow/frontend/static/img/layout-browser-hd-bg.gif differ diff --git a/htsworkflow/frontend/static/img/s.gif b/htsworkflow/frontend/static/img/s.gif new file mode 100644 index 0000000..1d11fa9 Binary files /dev/null and b/htsworkflow/frontend/static/img/s.gif differ diff --git a/htsworkflow/frontend/static/js/htsw.js b/htsworkflow/frontend/static/js/htsw.js new file mode 100644 index 0000000..3f1e243 --- /dev/null +++ b/htsworkflow/frontend/static/js/htsw.js @@ -0,0 +1,198 @@ +Ext.state.Manager.setProvider(new Ext.state.CookieProvider()); + +Ext.override(Ext.Panel,{ + getState: function() { + return { collapsed: this.collapsed }; + } +}); + +var quick_msg = function(msg) +{ + Ext.MessageBox.show({ + title: 'Info', + msg: msg, + buttons: Ext.MessageBox.OK, + icon: Ext.MessageBox.INFO + }); +} + +var goto_url = function(www_url) +{ + window.location = www_url; +} + +$(document).ready(function(){ + + //---------------------------------------- + // Dynamically Generate Panels from HTML! + var create_dynamic_panels = function(){ + var wp_items = Ext.fly('west_panel_items'); + var ul_items = wp_items.query('ul'); + + var dynamic_panels = new Array(); + Ext.each(ul_items, function(ul) { + var panel_obj = new Ext.Panel({ + frame: true, + title: ul.id, + //collapsible: true, + //titleCollapse: true, + //collapsed: true, + //stateful: true, + //stateId: 'freezer_panel_state', + margins: '0 0 0 0', + //width: 200, + contentEl: ul + //stateEvents: ['collapse', 'expand'] + }); + dynamic_panels.push(panel_obj); + }); + return dynamic_panels; + } + + var panel_bcmagic = new Ext.Panel({ + //title: 'BC Magic', + unstyled: true, + contentEl: 'bcmagic_div', + height: 180 + }); + + var menuPanel = new Ext.Panel({ + id: 'menu_panel', + region: 'west', + collapsible: true, + split: true, + margins: '4 0 0 0', + //cmargins: '2 2 0 2', + collapseMode: 'mini', + width: 200, + minWidth: 150, + border: false, + //baseCls: 'x-plain', + unstyled: true, + layout: 'vbox', + layoutConfig: { + align: 'stretch', + pack: 'start' + }, + // Add dynamically generated panels from html and include barcode magic + items: create_dynamic_panels().concat([panel_bcmagic]) + }); + + //------------------------------- + // Main Viewport Setup + //------------------------------- + var mainBorderPanel = new Ext.Viewport({ + layout: 'border', + items: [{ + region: 'north', + layout: 'vBox', + layoutConfig: { + align: 'stretch', + pack: 'start' + }, + items: [{ + xtype: 'box', + applyTo: 'header', + id: 'header-panel', + height: 30 + },{ + id: 'main_toolbar', + xtype: 'toolbar', + //height: 100, + /*items: [{ + text: "Demo Button", + handler: function() { quick_msg('Messages can be fun!'); } + }],*/ + margins: '2 0 0 0' + }], + height: 60 + },menuPanel,{ + //title: 'Body', + region: 'center', + xtype: 'panel', + //autoScroll: true, + layout: 'fit', + margins: '2 2 2 2', + items: { + //title: 'Inner Panel', + contentEl: 'body_content', + border: true, + autoScroll: true + } + }] + }); + + //------------------------------- + // Menu Bar Setup + //------------------------------- + var main_tb = Ext.getCmp('main_toolbar'); + + var add_buttons_from_html_left = function(main_tb){ + var left_tbar_data = Ext.fly('left_tbar_data'); + var div_array = left_tbar_data.query('div'); + var div_id = null; + // Loop through each div since it defines a button and link or a spacer and add it to the right side of the toolbar + Ext.each(div_array, function(divobj) { + div_id = divobj.id; + if (div_id == 'spacer'){ + main_tb.add('-'); + } else { + main_tb.add({ + text: div_id, + handler: function() { goto_url(divobj.getAttribute('href')); } + }); + } + }); + //return right_tbar_data; + } + + var add_buttons_from_html_right = function(main_tb){ + var right_tbar_data = Ext.fly('right_tbar_data'); + var div_array = right_tbar_data.query('div'); + var div_id = null; + // Loop through each div since it defines a button and link or a spacer and add it to the right side of the toolbar + Ext.each(div_array, function(divobj) { + div_id = divobj.id; + if (div_id == 'spacer'){ + main_tb.add('-'); + } else { + main_tb.add({ + text: div_id, + handler: function() { goto_url(divobj.getAttribute('href')); } + }); + } + }); + //return right_tbar_data; + } + + add_buttons_from_html_left(main_tb); + + // Shifts the remaining toolbar options to the right side. + main_tb.add({ xtype: 'tbfill' }); + var user_info = Ext.fly('login_info'); + var logout_url = user_info.getAttribute('logouturl'); + var login_url = user_info.getAttribute('loginurl'); + + if (user_info.getAttribute('authenticated') == 'true') { + main_tb.add({ + xtype: 'tbtext', + text: 'User: ' + user_info.getAttribute('user') + }); + main_tb.add('-'); + add_buttons_from_html_right(main_tb); + main_tb.add('-'); + main_tb.add({ + text: 'Logout', + handler: function() { goto_url(logout_url); } + }); + + } else { + main_tb.add({ + text: 'Login', + handler: function() { goto_url(login_url) } + }); + } + + main_tb.doLayout(); + +}); \ No newline at end of file diff --git a/htsworkflow/frontend/static/js/jquery-1.3.2.js b/htsworkflow/frontend/static/js/jquery-1.3.2.js new file mode 100644 index 0000000..9263574 --- /dev/null +++ b/htsworkflow/frontend/static/js/jquery-1.3.2.js @@ -0,0 +1,4376 @@ +/*! + * jQuery JavaScript Library v1.3.2 + * http://jquery.com/ + * + * Copyright (c) 2009 John Resig + * Dual licensed under the MIT and GPL licenses. + * http://docs.jquery.com/License + * + * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) + * Revision: 6246 + */ +(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.html + 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 + ">"; + }); + + // 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("", "" ] || + + !tags.indexOf("", "" ] || + + tags.match(/^<(thead|tbody|tfoot|colg|cap)/) && + [ 1, "", "
" ] || + + !tags.indexOf("", "" ] || + + // matched above + (!tags.indexOf("", "" ] || + + !tags.indexOf("", "" ] || + + // IE can't serialize and + + + + + + + {{ bcmagic.media }} + + + + + +
+ {% block dynamic_panels %} + + + + {% endblock %} +
+ + +
+ {% include "magic.html" %} +
+ + +
+ {% block left_tbar_menu %} +
+ {% endblock %} +
+ + +
+ {% block right_tbar_menu %} + +
+
+
+ {% endblock %} +
+ + +
+ + +
+ {% block msg %} +
{{ msg }}
+ {% endblock %} + {% block content %} + {{ body }} + {% endblock %} +
+ + diff --git a/htsworkflow/frontend/templates/base.html b/htsworkflow/frontend/templates/base.html index 5899e59..311eb20 100644 --- a/htsworkflow/frontend/templates/base.html +++ b/htsworkflow/frontend/templates/base.html @@ -1,22 +1,9 @@ - - - -{% block title %}{% endblock %} - -{% if LANGUAGE_BIDI %}{% endif %} -{% block extrastyle %}{% endblock %} -{% block extrahead %}{% endblock %} -{% block blockbots %}{% endblock %} - {% load i18n %} - - -
- + + - - - diff --git a/htsworkflow/frontend/templates/base_site.html b/htsworkflow/frontend/templates/base_site.html index 3cf8738..81b4f47 100644 --- a/htsworkflow/frontend/templates/base_site.html +++ b/htsworkflow/frontend/templates/base_site.html @@ -1,10 +1,10 @@ -{% extends "admin/base.html" %} +{% extends "base.html" %} {% load i18n %} {% block title %}{{ sitename }}{% endblock %} {% block branding %} -

HTS Workflow

+

HTSWorkflow

{% endblock %} {% block nav-global %}{% endblock %} diff --git a/htsworkflow/frontend/templates/magic.html b/htsworkflow/frontend/templates/magic.html new file mode 100644 index 0000000..e1d1061 --- /dev/null +++ b/htsworkflow/frontend/templates/magic.html @@ -0,0 +1,3 @@ +
+{{ bcmagic }}
+
diff --git a/htsworkflow/frontend/templates/registration/login.html b/htsworkflow/frontend/templates/registration/login.html new file mode 100644 index 0000000..5442caf --- /dev/null +++ b/htsworkflow/frontend/templates/registration/login.html @@ -0,0 +1,29 @@ +{% extends "flowcell_libraries_app.html" %} + +{% block title %} +Login +{% endblock %} + +{% block content %} + +{% if form.errors %} +

Your username and password didn't match. Please try again.

+{% endif %} + +
+ + + + + + + + + +
{{ form.username.label_tag }}{{ form.username }}
{{ form.password.label_tag }}{{ form.password }}
+ + + +
+ +{% endblock %} diff --git a/htsworkflow/frontend/templates/registration/profile.html b/htsworkflow/frontend/templates/registration/profile.html new file mode 100644 index 0000000..c53c4de --- /dev/null +++ b/htsworkflow/frontend/templates/registration/profile.html @@ -0,0 +1,9 @@ +{% extends "flowcell_libraries_app.html" %} + +{% block content %} + + + + +
User Name:{{ user.username }}
Full Name:{{ user.get_full_name }}
E-mail:{{ user.email }}
+{% endblock %} diff --git a/htsworkflow/frontend/templates/samples/library_index.html b/htsworkflow/frontend/templates/samples/library_index.html index c0a37e6..62dc273 100644 --- a/htsworkflow/frontend/templates/samples/library_index.html +++ b/htsworkflow/frontend/templates/samples/library_index.html @@ -1,8 +1,6 @@ {% extends "base_site.html" %} {% load adminmedia admin_list i18n %} -{% block stylesheet %}{{ MEDIA_URL }}css/data-browse-index.css{% endblock %} - {% block bodyclass %}change-list{% endblock %} {% block coltype %}flex{% endblock %} @@ -26,8 +24,8 @@ {% block pagination %}{% pagination cl %}{% endblock %} {% block summary_stats %} - - +
+ @@ -37,18 +35,18 @@ - + {% for lib in library_list %} - - - - - - + + + + + + {% if lib.is_archived %} - + {% else %} - + {% endif %} {% endfor %} diff --git a/htsworkflow/frontend/urls.py b/htsworkflow/frontend/urls.py index f5f014c..4be3ed5 100644 --- a/htsworkflow/frontend/urls.py +++ b/htsworkflow/frontend/urls.py @@ -11,6 +11,12 @@ admin.autodiscover() from htsworkflow.frontend import settings urlpatterns = patterns('', + ('^accounts/login/$', 'django.contrib.auth.views.login'), + ('^accounts/logout/$', 'django.contrib.auth.views.logout'), + ('^accounts/logout_then_login/$', 'django.contrib.auth.views.logout_then_login'), + ('^accounts/pwchange/$', 'django.contrib.auth.views.password_change'), + ('^accounts/pwchange_done/$', 'django.contrib.auth.views.password_change_done'), + ('^accounts/profile/$', 'htsworkflow.frontend.samples.views.user_profile'), # Base: (r'^eland_config/', include('htsworkflow.frontend.eland_config.urls')), # Admin:
Parent Library IDHD
{{ lib.amplified_from }}{{ lib.library_id }}{{ lib.species_name }}{{ lib.library_name }}{{ lib.lanes_run }}
{{ lib.amplified_from }}{{ lib.library_id }}{{ lib.species_name }}{{ lib.library_name }}{{ lib.lanes_run }}ArchivedArchived