root/galaxy-central/static/scripts/galaxy.base.js @ 2

リビジョン 2, 22.8 KB (コミッタ: hatakeyama, 14 年 前)

import galaxy-central

行番号 
1// Returns the number of keys (elements) in an array/dictionary.
2function obj_length(obj) {
3    if (obj.length !== undefined) {
4        return obj.length;
5    }
6
7    var count = 0;
8    for (var element in obj) {
9        count++;
10    }
11    return count;
12}
13
14$.fn.makeAbsolute = function(rebase) {
15    return this.each(function() {
16        var el = $(this);
17        var pos = el.position();
18        el.css({
19            position: "absolute",
20            marginLeft: 0, marginTop: 0,
21            top: pos.top, left: pos.left,
22            right: $(window).width() - ( pos.left + el.width() )
23        });
24        if (rebase) {
25            el.remove().appendTo("body");
26        }
27    });
28};
29
30function ensure_popup_helper() {
31    // And the helper below the popup menus
32    if ( $( "#popup-helper" ).length === 0 ) {
33        $( "<div id='popup-helper'/>" ).css( {
34            background: 'white', opacity: 0, zIndex: 15000,
35            position: 'absolute', top: 0, left: 0, width: '100%', height: '100%'
36        } ).appendTo( "body" ).hide();
37    }
38}
39
40function attach_popupmenu( button_element, wrapper ) {
41    var clean = function() {
42        wrapper.unbind().hide();
43        $("#popup-helper").unbind( "click.popupmenu" ).hide();
44        // $(document).unbind( "click.popupmenu" );
45    };
46    var click_handler = function( e ) {
47        // var o = $(button_element).offset();
48        $("#popup-helper").bind( "click.popupmenu", clean ).show();
49        // $(document).bind( "click.popupmenu", clean );
50        // Show off screen to get size right
51        wrapper.click( clean ).css( { left: 0, top: -1000 } ).show();
52        // console.log( e.pageX, $(document).scrollLeft() + $(window).width(), $(menu_element).width() );
53        var x = e.pageX - wrapper.width() / 2 ;
54        x = Math.min( x, $(document).scrollLeft() + $(window).width() - $(wrapper).width() - 20 );
55        x = Math.max( x, $(document).scrollLeft() + 20 );
56        // console.log( e.pageX, $(document).scrollLeft() + $(window).width(), $(menu_element).width() );
57       
58       
59        wrapper.css( {
60            top: e.pageY - 5,
61            left: x
62        } );
63        return false;
64    };
65    $(button_element).bind("click", click_handler);
66}
67
68function make_popupmenu( button_element, options ) {
69    ensure_popup_helper();
70    // var container_element = $(button_element);
71    // if ( container_element.parent().hasClass( "combo-button" ) ) {
72    //    container_element = container_element.parent();
73    // }
74    // container_element).css( "position", "relative" );
75    var menu_element = $( "<ul id='" + button_element.attr('id') + "-menu'></ul>" );
76    if (obj_length(options) <= 0) {
77        $("<li/>").html("No options").appendTo(menu_element);
78    }
79    $.each( options, function( k, v ) {
80        if (v) {
81            $("<li/>").html(k).click(v).appendTo(menu_element);
82        } else {
83            $("<li class='head'/>").html(k).appendTo(menu_element);
84        }
85    });
86    var wrapper = $( "<div class='popmenu-wrapper'>" );
87    wrapper.append( menu_element )
88           .append( "<div class='overlay-border'>" )
89           .css( "position", "absolute" )
90           .appendTo( "body" )
91           .hide();
92    attach_popupmenu( button_element, wrapper );
93    return menu_element;
94}
95
96// Toggle popup menu options using regular expression on option names.
97function show_hide_popupmenu_options( menu, option_name_re, show ) {
98    var show = (show === undefined ? true : show );
99    var re = new RegExp(option_name_re);
100    $(menu).find("li").each( function() {
101        if ( re.exec( $(this).text() ) )
102            if (show)
103                $(this).show();
104            else
105                $(this).hide();
106    });
107}
108
109function make_popup_menus() {
110    jQuery( "div[popupmenu]" ).each( function() {
111        var options = {};
112        $(this).find( "a" ).each( function() {
113            var confirmtext = $(this).attr( "confirm" ),
114                href = $(this).attr( "href" ),
115                target = $(this).attr( "target" );
116            options[ $(this).text() ] = function() {
117                if ( !confirmtext || confirm( confirmtext ) ) {
118                    var f = window;
119                    if ( target == "_parent" ) {
120                        f = window.parent;
121                    } else if ( target == "_top" ) {
122                        f = window.top;
123                    }
124                    f.location = href;
125                }
126            };
127        });
128        var b = $( "#" + $(this).attr( 'popupmenu' ) );
129        b.find("a").bind("click", function(e) {
130            e.stopPropagation(); // Stop bubbling so clicking on the link goes through
131            return true;
132        });
133        make_popupmenu( b, options );
134        $(this).remove();
135        b.addClass( "popup" ).show();
136    });
137}
138
139// Alphanumeric/natural sort fn
140function naturalSort(a, b) {
141    // setup temp-scope variables for comparison evauluation
142    var re = /(-?[0-9\.]+)/g,
143        x = a.toString().toLowerCase() || '',
144        y = b.toString().toLowerCase() || '',
145        nC = String.fromCharCode(0),
146        xN = x.replace( re, nC + '$1' + nC ).split(nC),
147        yN = y.replace( re, nC + '$1' + nC ).split(nC),
148        xD = (new Date(x)).getTime(),
149        yD = xD ? (new Date(y)).getTime() : null;
150    // natural sorting of dates
151    if ( yD )
152        if ( xD < yD ) return -1;
153        else if ( xD > yD ) return 1;
154    // natural sorting through split numeric strings and default strings
155    for( var cLoc = 0, numS = Math.max(xN.length, yN.length); cLoc < numS; cLoc++ ) {
156        oFxNcL = parseFloat(xN[cLoc]) || xN[cLoc];
157        oFyNcL = parseFloat(yN[cLoc]) || yN[cLoc];
158        if (oFxNcL < oFyNcL) return -1;
159        else if (oFxNcL > oFyNcL) return 1;
160    }
161    return 0;
162}
163
164// Replace select box with a text input box + autocomplete.
165function replace_big_select_inputs(min_length, max_length) {
166    // To do replace, jQuery's autocomplete plugin must be loaded.
167    if (!jQuery().autocomplete)
168        return;
169   
170    // Set default for min_length and max_length
171    if (min_length === undefined)
172        min_length = 20;
173    if (max_length === undefined)
174        max_length = 3000;
175   
176    $('select').each( function() {
177        var select_elt = $(this);
178        // Make sure that options is within range.
179        var num_options = select_elt.find('option').length;
180        if ( (num_options < min_length) || (num_options > max_length) ) {
181            return;
182        }
183           
184        // Skip multi-select because widget cannot handle multi-select.
185        if (select_elt.attr('multiple') == true) {
186            return;
187        }
188           
189        if (select_elt.hasClass("no-autocomplete")) {
190            return;
191        }
192       
193        // Replace select with text + autocomplete.
194        var start_value = select_elt.attr('value');
195       
196        // Set up text input + autocomplete element.
197        var text_input_elt = $("<input type='text' class='text-and-autocomplete-select'></input>");
198        text_input_elt.attr('size', 40);
199        text_input_elt.attr('name', select_elt.attr('name'));
200        text_input_elt.attr('id', select_elt.attr('id'));
201        text_input_elt.click( function() {
202            // Show all. Also provide note that load is happening since this can be slow.
203            var cur_value = $(this).val();
204            $(this).val('Loading...');
205            $(this).showAllInCache();
206            $(this).val(cur_value);
207            $(this).select();
208        });
209
210        // Get options for select for autocomplete.
211        var select_options = [];
212        var select_mapping = {};
213        select_elt.children('option').each( function() {
214            // Get text, value for option.
215            var text = $(this).text();
216            var value = $(this).attr('value');
217
218            // Set options and mapping. Mapping is (i) [from text to value] AND (ii) [from value to value]. This
219            // enables a user to type the value directly rather than select the text that represents the value.
220            select_options.push( text );
221            select_mapping[ text ] = value;
222            select_mapping[ value ] = value;
223
224            // If this is the start value, set value of input element.
225            if ( value == start_value ) {
226                text_input_elt.attr('value', text);
227            }
228        });
229       
230        // Set initial text if it's empty.
231        if ( start_value == '' || start_value == '?') {
232            text_input_elt.attr('value', 'Click to Search or Select');
233        }
234       
235        // Sort dbkey options list only.
236        if (select_elt.attr('name') == 'dbkey')
237            select_options = select_options.sort(naturalSort);
238       
239        // Do autocomplete.
240        var autocomplete_options = { selectFirst: false, autoFill: false, mustMatch: false, matchContains: true, max: max_length, minChars : 0, hideForLessThanMinChars : false };
241        text_input_elt.autocomplete(select_options, autocomplete_options);
242
243        // Replace select with text input.
244        select_elt.replaceWith(text_input_elt);
245       
246        // Set trigger to replace text with value when element's form is submitted. If text doesn't correspond to value, default to start value.
247        var submit_hook = function() {
248            // Try to convert text to value.
249            var cur_value = text_input_elt.attr('value');
250            var new_value = select_mapping[cur_value];
251            if (new_value !== null && new_value !== undefined) {
252                text_input_elt.attr('value', new_value);
253            }
254            else {
255                // If there is a non-empty start value, use that; otherwise unknown.
256                if (start_value != "") {
257                    text_input_elt.attr('value', start_value);
258                } else {
259                    // This is needed to make the DB key work.
260                    text_input_elt.attr('value', '?');
261                }
262            }
263        };
264        text_input_elt.parents('form').submit( function() { submit_hook(); } );
265       
266        // Add custom event so that other objects can execute name --> value conversion whenever they want.
267        $(document).bind("convert_dbkeys", function() { submit_hook(); } );
268       
269        // If select is refresh on change, mirror this behavior.
270        if (select_elt.attr('refresh_on_change') == 'true') {
271            // Get refresh vals.
272            var ref_on_change_vals = select_elt.attr('refresh_on_change_values'),
273                last_selected_value = select_elt.attr("last_selected_value");
274            if (ref_on_change_vals !== undefined)
275                ref_on_change_vals = ref_on_change_vals.split(',');
276           
277            // Function that attempts to refresh based on the value in the text element.
278            var try_refresh_fn = function() {
279                // Get new value and see if it can be matched.
280                var new_value = select_mapping[text_input_elt.attr('value')];
281                if (new_value !== null && new_value !== undefined) {
282                    if ($.inArray(new_value, ref_on_change_vals) === -1 && $.inArray(last_selected_value, ref_on_change_vals) === -1) {
283                        return;
284                    }
285                    text_input_elt.attr('value', new_value);
286                    $(window).trigger("refresh_on_change");
287                    text_input_elt.parents('form').submit();
288                }
289            };
290           
291            // Attempt refresh if (a) result event fired by autocomplete (indicating autocomplete occurred) or (b) on keyup (in which
292            // case a user may have manually entered a value that needs to be refreshed).
293            text_input_elt.bind("result", try_refresh_fn);
294            text_input_elt.keyup( function(e) {
295                if (e.keyCode === 13) { // Return key
296                    try_refresh_fn();
297                }
298            });
299           
300            // Disable return key so that it does not submit the form automatically. This is done because element should behave like a
301            // select (enter = select), not text input (enter = submit form).
302            text_input_elt.keydown( function(e) {
303                if (e.keyCode === 13) { // Return key
304                    return false;
305                }
306            });
307        }
308    });
309}
310
311// Edit and save text asynchronously.
312function async_save_text(click_to_edit_elt, text_elt_id, save_url, text_parm_name, num_cols, use_textarea, num_rows, on_start, on_finish) {
313    // Set defaults if necessary.
314    if (num_cols === undefined) {
315        num_cols = 30;
316    }
317    if (num_rows === undefined) {
318        num_rows = 4;
319    }
320   
321    // Set up input element.
322    $("#" + click_to_edit_elt).live( "click", function() {
323        // Check if this is already active
324        if ( $("#renaming-active").length > 0) {
325            return;
326        }
327        var text_elt = $("#" + text_elt_id),
328            old_text = text_elt.text(),
329            t;
330           
331        if (use_textarea) {
332            t = $("<textarea></textarea>").attr({ rows: num_rows, cols: num_cols }).text( $.trim(old_text) );
333        } else {
334            t = $("<input type='text'></input>").attr({ value: $.trim(old_text), size: num_cols });
335        }
336        t.attr("id", "renaming-active");
337        t.blur( function() {
338            $(this).remove();
339            text_elt.show();
340            if (on_finish) {
341                on_finish(t);
342            }
343        });
344        t.keyup( function( e ) {
345            if ( e.keyCode === 27 ) {
346                // Escape key
347                $(this).trigger( "blur" );
348            } else if ( e.keyCode === 13 ) {
349                // Enter key submits
350                var ajax_data = {};
351                ajax_data[text_parm_name] = $(this).val();
352                $(this).trigger( "blur" );
353                $.ajax({
354                    url: save_url,
355                    data: ajax_data,
356                    error: function() {
357                        alert( "Text editing for elt " + text_elt_id + " failed" );
358                        // TODO: call finish or no? For now, let's not because error occurred.
359                    },
360                    success: function(processed_text) {
361                        // Set new text and call finish method.
362                        if (processed_text != "")
363                            text_elt.text(processed_text);
364                        else
365                            text_elt.html("<em>None</em>");
366                        if (on_finish) {
367                            on_finish(t);
368                        }
369                    }
370                });
371            }
372        });
373       
374        if (on_start) {
375            on_start(t);
376        }
377        // Replace text with input object and focus & select.
378        text_elt.hide();
379        t.insertAfter( text_elt );
380        t.focus();
381        t.select();
382       
383        return;
384    });
385}
386
387function init_history_items(historywrapper, noinit, nochanges) {
388
389    var action = function() {
390        // Load saved state and show as necessary
391        try {
392            var stored = $.jStore.store("history_expand_state");
393            if (stored) {
394                for (var id in stored) {
395                    $("#" + id + " div.historyItemBody" ).show();
396                }
397            }
398        } catch(err) {
399            // Something was wrong with values in storage, so clear storage
400            $.jStore.remove("history_expand_state");
401        }
402
403        // If Mozilla, hide scrollbars in hidden items since they cause animation bugs
404        if ( $.browser.mozilla ) {
405            $( "div.historyItemBody" ).each( function() {
406                if ( ! $(this).is( ":visible" ) ) $(this).find( "pre.peek" ).css( "overflow", "hidden" );
407            })
408        }
409       
410        historywrapper.each( function() {
411            var id = this.id;
412            var body = $(this).children( "div.historyItemBody" );
413            var peek = body.find( "pre.peek" )
414            $(this).find( ".historyItemTitleBar > .historyItemTitle" ).wrap( "<a href='javascript:void(0);'></a>" ).click( function() {
415                if ( body.is(":visible") ) {
416                    // Hiding stuff here
417                    if ( $.browser.mozilla ) { peek.css( "overflow", "hidden" ); }
418                    body.slideUp( "fast" );
419                   
420                    if (!nochanges) { // Ignore embedded item actions
421                        // Save setting
422                        var prefs = $.jStore.store("history_expand_state");
423                        if (prefs) {
424                            delete prefs[id];
425                            $.jStore.store("history_expand_state", prefs);
426                        }
427                    }
428                } else {
429                    // Showing stuff here
430                    body.slideDown( "fast", function() {
431                        if ( $.browser.mozilla ) { peek.css( "overflow", "auto" ); }
432                    });
433                   
434                    if (!nochanges) {
435                        // Save setting
436                        var prefs = $.jStore.store("history_expand_state");
437                        if (prefs === undefined) { prefs = {}; }
438                        prefs[id] = true;
439                        $.jStore.store("history_expand_state", prefs);
440                    }
441                }
442                return false;
443            });
444        });
445       
446        // Generate 'collapse all' link
447        $("#top-links > a.toggle").click( function() {
448            var prefs = $.jStore.store("history_expand_state");
449            if (prefs === undefined) { prefs = {}; }
450            $( "div.historyItemBody:visible" ).each( function() {
451                if ( $.browser.mozilla ) {
452                    $(this).find( "pre.peek" ).css( "overflow", "hidden" );
453                }
454                $(this).slideUp( "fast" );
455                if (prefs) {
456                    delete prefs[$(this).parent().attr("id")];
457                }
458            });
459            $.jStore.store("history_expand_state", prefs);
460        }).show();
461    }
462   
463    if (noinit) {
464        action();
465    } else {
466        // Load jStore for local storage
467        $.jStore.init("galaxy"); // Auto-select best storage
468        $.jStore.engineReady(function() {
469            action();
470        });
471    }
472}
473
474function commatize( number ) {
475    number += ''; // Convert to string
476    var rgx = /(\d+)(\d{3})/;
477    while (rgx.test(number)) {
478        number = number.replace(rgx, '$1' + ',' + '$2');
479    }
480    return number;
481}
482
483// Reset tool search to start state.
484function reset_tool_search( initValue ) {
485    // Function may be called in top frame or in tool_menu_frame;
486    // in either case, get the tool menu frame.
487    var tool_menu_frame = $("#galaxy_tools").contents();
488    if (tool_menu_frame.length == 0) {
489        tool_menu_frame = $(document);
490    }
491       
492    // Remove classes that indicate searching is active.
493    $(this).removeClass("search_active");
494    tool_menu_frame.find(".toolTitle").removeClass("search_match");
495   
496    // Reset visibility of tools and labels.
497    tool_menu_frame.find(".toolSectionBody").hide();
498    tool_menu_frame.find(".toolTitle").show();
499    tool_menu_frame.find(".toolPanelLabel").show();
500    tool_menu_frame.find(".toolSectionWrapper").each( function() {
501        if ($(this).attr('id') != 'recently_used_wrapper') {
502            // Default action.
503            $(this).show();
504        } else if ($(this).hasClass("user_pref_visible")) {
505            $(this).show();
506        }
507    });
508    tool_menu_frame.find("#search-no-results").hide();
509   
510    // Reset search input.
511    tool_menu_frame.find("#search-spinner").hide();
512    if (initValue) {
513        var search_input = tool_menu_frame.find("#tool-search-query");
514        search_input.val("search tools");
515        search_input.css("font-style", "italic");
516    }
517}
518
519// Create GalaxyAsync object.
520var GalaxyAsync = function(log_action) {
521    this.url_dict = {};
522    this.log_action = (log_action === undefined ? false : log_action);
523}
524
525GalaxyAsync.prototype.set_func_url = function( func_name, url ) {
526    this.url_dict[func_name] = url;
527};
528
529// Set user preference asynchronously.
530GalaxyAsync.prototype.set_user_pref = function( pref_name, pref_value ) {
531    // Get URL.
532    var url = this.url_dict[arguments.callee];
533    if (url === undefined) { return false; }
534    $.ajax({                   
535        url: url,
536        data: { "pref_name" : pref_name, "pref_value" : pref_value },
537        error: function() { return false; },
538        success: function() { return true; }                                           
539    });
540};
541
542// Log user action asynchronously.
543GalaxyAsync.prototype.log_user_action = function( action, context, params ) {
544    if (!this.log_action) { return; }
545       
546    // Get URL.
547    var url = this.url_dict[arguments.callee];
548    if (url === undefined) { return false; }
549    $.ajax({                   
550        url: url,
551        data: { "action" : action, "context" : context, "params" : params },
552        error: function() { return false; },
553        success: function() { return true; }                                           
554    });
555};
556
557// Add to trackster browser functionality
558$(".trackster-add").live("click", function() {
559    var dataset = this,
560        dataset_jquery = $(this);
561    $.ajax({
562        url: dataset_jquery.attr("data-url"),
563        dataType: "html",
564        error: function() { alert( "Could not add this dataset to browser." ); },
565        success: function(table_html) {
566            var parent = window.parent;
567            parent.show_modal("Add to Browser:", table_html, {
568                "Cancel": function() {
569                    parent.hide_modal();
570                },
571                "Insert into selected": function() {
572                    $(parent.document).find('input[name=id]:checked').each(function() {
573                        var vis_id = $(this).val();
574                        parent.location = dataset_jquery.attr("action-url") + "&id=" + vis_id;
575                    });
576                },
577                "Insert into new browser": function() {
578                    parent.location = dataset_jquery.attr("new-url");
579                }
580            });
581        }
582    });
583});
584
585
586
587$(document).ready( function() {
588    $("select[refresh_on_change='true']").change( function() {
589        var select_field = $(this),
590            select_val = select_field.val(),
591            refresh = false,
592            ref_on_change_vals = select_field.attr("refresh_on_change_values");
593        if (ref_on_change_vals) {
594            ref_on_change_vals = ref_on_change_vals.split(',');
595            var last_selected_value = select_field.attr("last_selected_value");
596            if ($.inArray(select_val, ref_on_change_vals) === -1 && $.inArray(last_selected_value, ref_on_change_vals) === -1) {
597                return;
598            }
599        }
600        $(window).trigger("refresh_on_change");
601        select_field.get(0).form.submit();
602    });
603   
604    // Links with confirmation
605    $( "a[confirm]" ).click( function() {
606        return confirm( $(this).attr("confirm") );
607    });
608    // Tooltips
609    if ( $.fn.tipsy ) {
610        $(".tooltip").tipsy( { gravity: 's' } );
611    }
612    // Make popup menus.
613    make_popup_menus();
614   
615    // Replace big selects.
616    replace_big_select_inputs(20, 1500);
617});
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。