Mercurial > repos > rlegendre > ribo_tools
changeset 0:01b945ba3697 draft default tip
Uploaded
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ribo_tools/get_codon_frequency.py Wed Sep 27 04:59:10 2017 -0400 @@ -0,0 +1,736 @@ +#!/usr/bin/env python2.7 +# -*- coding: utf-8 -*- + +''' + Created on sep. 2013 + @author: rachel legendre + @copyright: rachel.legendre@igmors.u-psud.fr + @license: GPL v3 +''' + +from __future__ import division +import os, sys, optparse, tempfile, subprocess, re, shutil, commands, urllib, time +import itertools +from math import log10 +from decimal import Decimal +from Bio import SeqIO +from Bio.Seq import Seq +from numpy import arange, std, array, linspace, average +#from matplotlib import pyplot as pl +import matplotlib +matplotlib.use('Agg') +import matplotlib.pyplot as pl +from matplotlib import font_manager +from matplotlib import colors +import csv +from scipy import stats, errstate +from collections import OrderedDict +import ribo_functions +import HTSeq +# #libraries for debugg +#import pdb +import cPickle + +def stop_err(msg): + sys.stderr.write("%s\n" % msg) + sys.stderr.write("Programme aborted at %s\n" % time.asctime(time.localtime(time.time()))) + sys.exit() + + +def init_codon_dict(): + + Codon_dict = OrderedDict([('AAA', 0), ('AAC', 0), ('AAG', 0), ('AAT', 0), ('ACA', 0), ('ACC', 0), ('ACG', 0), ('ACT', 0), ('AGA', 0), ('AGC', 0), ('AGG', 0), ('AGT', 0), ('ATA', 0), ('ATC', 0), ('ATG', 0), ('ATT', 0), ('CAA', 0), ('CAC', 0), ('CAG', 0), ('CAT', 0), ('CCA', 0), ('CCC', 0), ('CCG', 0), ('CCT', 0), ('CGA', 0), ('CGC', 0), ('CGG', 0), ('CGT', 0), ('CTA', 0), ('CTC', 0), ('CTG', 0), ('CTT', 0), ('GAA', 0), ('GAC', 0), ('GAG', 0), ('GAT', 0), ('GCA', 0), ('GCC', 0), ('GCG', 0), ('GCT', 0), ('GGA', 0), ('GGC', 0), ('GGG', 0), ('GGT', 0), ('GTA', 0), ('GTC', 0), ('GTG', 0), ('GTT', 0), ('TAA', 0), ('TAC', 0), ('TAG', 0), ('TAT', 0), ('TCA', 0), ('TCC', 0), ('TCG', 0), ('TCT', 0), ('TGA', 0), ('TGC', 0), ('TGG', 0), ('TGT', 0), ('TTA', 0), ('TTC', 0), ('TTG', 0), ('TTT', 0)]) + return Codon_dict + + + +def get_codon_usage(bamfile, GFF, site, kmer, a_pos): + ''' + Read GFF dict and get gene codon usage. + Return dict of codons usage + ''' + try: + codon = init_codon_dict() + multi_tag = "XS:i:" ## bowtie Tag + tag = "IH:i:1" ## RUM tag + + for feature in GFF : + if feature.type == 'gene' : + codon_dict = init_codon_dict() + chrom = feature.iv.chrom + start = feature.iv.start + stop = feature.iv.end + if start+50 < stop-50 : + region = chrom + ':' + str(start+50) + '-' + str(stop-50) + # #get all reads in this gene + reads = subprocess.check_output(["samtools", "view", bamfile, region]) + head = subprocess.check_output(["samtools", "view", "-H", bamfile]) + read_tab = reads.split('\n') + for read in read_tab: + # # search mapper for eliminate multiple alignements + if 'bowtie' in head: + multi_tag = "XS:i:" + elif 'bwa' in head: + multi_tag = "XT:A:R" + elif 'TopHat' in head: + tag = "NH:i:1" + else : + stop_err("No PG tag find in "+samfile+". Please use bowtie, bwa or Tophat for mapping") + + if len(read) == 0: + continue + len_read = len(read.split('\t')[9]) + # if it's read of good length + if len_read == kmer and (tag in read or multi_tag not in read): + feat = read.split('\t') + seq = feat[9] + # if it's a reverse read + if feat[1] == '16' : + if site == "A" : + # #get A-site + cod = str(Seq(seq[a_pos-5:a_pos-2]).reverse_complement()) + elif site == "P" : + # #get P-site + cod = str(Seq(seq[a_pos-2:a_pos+1]).reverse_complement()) + else : + # #get site-E + cod = str(Seq(seq[a_pos+1:a_pos+4]).reverse_complement()) + # # test if it's a true codon not a CNG codon for example + if codon_dict.has_key(cod) : + codon_dict[cod] += 1 + # if it's a forward read + elif feat[1] == '0' : + if site == "A" : + # #get A-site + cod = seq[a_pos:a_pos+3] + elif site == "P" : + # #get P-site + cod = seq[a_pos-3:a_pos] + else : + # #get site-E + cod = seq[a_pos-6:a_pos-3] + if codon_dict.has_key(cod) : + codon_dict[cod] += 1 + del(read) + # # add in global dict + for cod, count in codon_dict.iteritems() : + codon[cod] += count + if sum(codon.values()) == 0 : + stop_err('There are no reads aligning on annotated genes in your GFF file') + else : + return codon + + except Exception, e: + stop_err('Error during codon usage calcul: ' + str(e)) + + + + +''' +http://pyinsci.blogspot.fr/2009/09/violin-plot-with-matplotlib.html +''' +def violin_plot(ax, data, pos, bp=False): + ''' + create violin plots on an axis + ''' + dist = max(pos) - min(pos) + w = min(0.15 * max(dist, 1.0), 0.5) + for d, p in zip(data, pos): + k = stats.gaussian_kde(d) # calculates the kernel density + m = k.dataset.min() # lower bound of violin + M = k.dataset.max() # upper bound of violin + x = arange(m, M, (M - m) / 100.) # support for violin + v = k.evaluate(x) # violin profile (density curve) + v = v / v.max() * w # scaling the violin to the available space + ax.fill_betweenx(x, p, v + p, facecolor=color1, alpha=0.3) + ax.fill_betweenx(x, p, -v + p, facecolor=color2, alpha=0.3) + if bp: + ax.boxplot(data, notch=1, positions=pos, vert=1) + + + +''' +http://log.ooz.ie/2013/02/matplotlib-comparative-histogram-recipe.html +''' +def comphist(x1, x2, orientation='vertical', **kwargs): + """Draw a comparative histogram.""" + # Split keyword args: + kwargs1 = {} + kwargs2 = {} + kwcommon = {} + for arg in kwargs: + tgt_arg = arg[:-1] + if arg.endswith('1'): + arg_dict = kwargs1 + elif arg.endswith('2'): + arg_dict = kwargs2 + else: + arg_dict = kwcommon + tgt_arg = arg + arg_dict[tgt_arg] = kwargs[arg] + kwargs1.update(kwcommon) + kwargs2.update(kwcommon) + + fig = pl.figure() + + # Have both histograms share one axis. + if orientation == 'vertical': + ax1 = pl.subplot(211) + ax2 = pl.subplot(212, sharex=ax1) + # Flip the ax2 histogram horizontally. + ax2.set_ylim(ax1.get_ylim()[::-1]) + pl.setp(ax1.get_xticklabels(), visible=False) + legend_loc = (1, 4) + else: + ax1 = pl.subplot(122) + ax2 = pl.subplot(121, sharey=ax1) + # Flip the ax2 histogram vertically. + ax2.set_xlim(ax2.get_xlim()[::-1]) + pl.setp(ax1.get_yticklabels(), visible=False) + legend_loc = (1, 2) + + ax1.hist(x1, orientation=orientation, **kwargs1) + ax2.hist(x2, orientation=orientation, **kwargs2) + ax2.set_ylim(ax1.get_ylim()[::-1]) + ax1.legend(loc=legend_loc[0]) + ax2.legend(loc=legend_loc[1]) + # Tighten up the layout. + pl.subplots_adjust(wspace=0.0, hspace=0.0) + return fig + + +def compute_FC_plot(cond1_norm, cond2_norm, cod_name, codon_to_test, dirout): + + FC_tab = [] + for z, y in zip(cond1_norm.itervalues(), cond2_norm.itervalues()): + fc = z - y + FC_tab.append(fc) + # #codon_to_test = ['TGA','TAG','TAA'] + + a = [] + b = [] + cod = [] + for codon in cond1_norm.iterkeys(): + if codon in codon_to_test : + fc = cond1_norm[codon] - cond2_norm[codon] + b.append(fc) + cod.append(codon) + else : + fc = cond1_norm[codon] - cond2_norm[codon] + a.append(fc) + + + fig = pl.figure(num=1) + comphist(array(a), array(b), label1='All codon', label2=cod_name, color2='green', bins=30, rwidth=1) + # pl.show() + pl.savefig(dirout + '/hist_codon_fc.png', format="png", dpi=340) + pl.clf() + + + # #violin plot + pos = range(2) + dat = array([array(a), array(b)]) + fig = pl.figure() + pl.title("Distribution of codons FoldChange between two conditions") + ax = fig.add_subplot(1, 1, 1) + lab = array(['All codons', cod_name]) + violin_plot(ax, dat, pos, bp=1) + for x, z in zip(dat, pos): + ax.plot(z, average(x), color='r', marker='*', markeredgecolor='r') + xtickNames = pl.setp(ax, xticklabels=lab) + pl.savefig(dirout + '/violinplot_codon.png', format="png", dpi=340) + pl.clf() + + # (Fval,pval) = stats.ttest_ind(a, b, axis=0, equal_var=True) + (Fval, pval) = stats.mannwhitneyu(a, b) + return pval + + +def get_aa_dict(cond1_norm, cond2_norm): + + # ## create amino acid dictionnary: + AA = OrderedDict({}) + AA['Phe'] = [cond1_norm['TTT'] + cond1_norm['TTC'], cond2_norm['TTT'] + cond2_norm['TTC']] + AA['Leu'] = [cond1_norm['TTA'] + cond1_norm['TTG'] + cond1_norm['CTT'] + cond1_norm['CTC'] + cond1_norm['CTA'] + cond1_norm['CTG'], cond2_norm['TTA'] + cond2_norm['TTG'] + cond2_norm['CTT'] + cond2_norm['CTC'] + cond2_norm['CTA'] + cond2_norm['CTG']] + AA['Ile'] = [cond1_norm['ATT'] + cond1_norm['ATC'] + cond1_norm['ATA'], cond2_norm['ATT'] + cond2_norm['ATC'] + cond2_norm['ATA']] + AA['Met'] = [cond1_norm['ATG'], cond2_norm['ATG']] + AA['Val'] = [cond1_norm['GTT'] + cond1_norm['GTC'] + cond1_norm['GTA'] + cond1_norm['GTG'] + cond1_norm['AGT'] + cond1_norm['AGC'], cond2_norm['GTT'] + cond2_norm['GTC'] + cond2_norm['GTA'] + cond2_norm['GTG'] + cond2_norm['AGT'] + cond2_norm['AGC']] + AA['Ser'] = [cond1_norm['TCT'] + cond1_norm['TCC'] + cond1_norm['TCA'] + cond1_norm['TCG'], cond2_norm['TCT'] + cond2_norm['TCC'] + cond2_norm['TCA'] + cond2_norm['TCG']] + AA['Pro'] = [cond1_norm['CCT'] + cond1_norm['CCC'] + cond1_norm['CCA'] + cond1_norm['CCG'], cond2_norm['CCT'] + cond2_norm['CCC'] + cond2_norm['CCA'] + cond2_norm['CCG']] + AA['Thr'] = [cond1_norm['ACT'] + cond1_norm['ACC'] + cond1_norm['ACA'] + cond1_norm['ACG'], cond2_norm['ACT'] + cond2_norm['ACC'] + cond2_norm['ACA'] + cond2_norm['ACG']] + AA['Ala'] = [cond1_norm['GCT'] + cond1_norm['GCC'] + cond1_norm['GCA'] + cond1_norm['GCG'], cond2_norm['GCT'] + cond2_norm['GCC'] + cond2_norm['GCA'] + cond2_norm['GCG']] + AA['Tyr'] = [cond1_norm['TAT'] + cond1_norm['TAC'], cond2_norm['TAT'] + cond2_norm['TAC']] + AA['Stop'] = [cond1_norm['TAA'] + cond1_norm['TAG'] + cond1_norm['TGA'], cond2_norm['TAA'] + cond2_norm['TAG'] + cond2_norm['TGA']] + AA['His'] = [cond1_norm['CAT'] + cond1_norm['CAC'], cond2_norm['CAT'] + cond2_norm['CAC']] + AA['Gln'] = [cond1_norm['CAA'] + cond1_norm['CAG'], cond2_norm['CAA'] + cond2_norm['CAG']] + AA['Asn'] = [cond1_norm['AAT'] + cond1_norm['AAC'], cond2_norm['AAT'] + cond2_norm['AAC']] + AA['Lys'] = [cond1_norm['AAA'] + cond1_norm['AAG'], cond2_norm['AAA'] + cond2_norm['AAG']] + AA['Asp'] = [cond1_norm['GAT'] + cond1_norm['GAC'], cond2_norm['GAT'] + cond2_norm['GAC']] + AA['Glu'] = [cond1_norm['GAA'] + cond1_norm['GAG'], cond2_norm['GAA'] + cond2_norm['GAG']] + AA['Cys'] = [cond1_norm['TGT'] + cond1_norm['TGC'], cond2_norm['TGT'] + cond2_norm['TGC']] + AA['Trp'] = [cond1_norm['TGG'], cond2_norm['TGG']] + AA['Arg'] = [cond1_norm['CGT'] + cond1_norm['CGC'] + cond1_norm['CGA'] + cond1_norm['CGG'] + cond1_norm['AGA'] + cond1_norm['AGG'], cond2_norm['CGT'] + cond2_norm['CGC'] + cond2_norm['CGA'] + cond2_norm['CGG'] + cond2_norm['AGA'] + cond2_norm['AGG']] + AA['Gly'] = [cond1_norm['GGT'] + cond1_norm['GGC'] + cond1_norm['GGA'] + cond1_norm['GGG'], cond2_norm['GGT'] + cond2_norm['GGC'] + cond2_norm['GGA'] + cond2_norm['GGG']] + + + return AA + + + +def plot_codon_usage(result, dirout, c1, c2, outfile, color1, color2): + ''' + Take list of dict of codon usage and use matplotlib for do graph + ''' + + # #if there are replicat + if len(result) == 4 : + # store each dict in variables to make code more readable + cond1_1 = result[0].copy() + cond1_2 = result[1].copy() + cond2_1 = result[2].copy() + cond2_2 = result[3].copy() + # get codon order in one of list + codon_sorted = sorted(cond1_1.iterkeys(), reverse=False) + try: + # get max of each list + sum11 = sum(list(cond1_1.itervalues())) + sum12 = sum(list(cond1_2.itervalues())) + sum21 = sum(list(cond2_1.itervalues())) + sum22 = sum(list(cond2_2.itervalues())) + # for each codon, get values and sd in each condition + cond1_val = {} + cond1 = {} + cond2_val = {} + cond2 = {} + std_cond1 = [] + std_cond2 = [] + max_val = [] # # max value for graph + for i in codon_sorted: + # # cond1 = mean of replicats cond1 divided by max + cond1_val[i] = ((cond1_1[i] / sum11 + cond1_2[i] / sum12) / 2) + cond1[i] = ((cond1_1[i] + cond1_2[i]) / 2) + # # standard deviation = absolute value of difference between replicats of cond1 + std_cond1.append(std(array([(cond1_1[i] * 100 / sum11), (cond1_2[i] * 100 / sum12)]))) + # # cond2 = mean of replicats cond1divided by max + cond2_val[i] = ((cond2_1[i] / sum21 + cond2_2[i] / sum22) / 2) + cond2[i] = ((cond2_1[i] + cond2_2[i]) / 2) + # # standard deviation = absolute value of difference between replicats of cond2 + std_cond2.append(std(array([((cond2_1[i]) * 100 / sum21), ((cond2_2[i]) * 100 / sum22)]))) + # # max value for each codon + max_val.append(max((cond1_1[i] / sum11 + cond1_2[i] / sum12) / 2, (cond2_1[i] / sum21 + cond2_2[i] / sum22) / 2)) + + # for graph design + cond1_norm = OrderedDict(sorted(cond1_val.items(), key=lambda t: t[0])) + cond1_norm.update ((x, y * 100) for x, y in cond1_norm.items()) + cond2_norm = OrderedDict(sorted(cond2_val.items(), key=lambda t: t[0])) + cond2_norm.update ((x, y * 100) for x, y in cond2_norm.items()) + max_val = [x * 100 for x in max_val] + except ZeroDivisionError: + stop_err("Not enough reads to compute the codon occupancy") + + AA = get_aa_dict(cond1_norm, cond2_norm) + max_valaa = [] + cond1_aa = [] + cond2_aa = [] + aa_name = list(AA.iterkeys()) + for z in AA.itervalues(): + cond1_aa.append(z[0]) + cond2_aa.append(z[1]) + max_valaa.append(max(z)) + # # plot amino acid profile : + fig = pl.figure(figsize=(15,10), num=1) + width = .50 + ax = fig.add_subplot(111) + ax.xaxis.set_ticks([]) + ind = arange(21) + pl.xlim(0, 21) + ax.bar(ind, cond1_aa, width, facecolor=color1, label=c1) + ax.bar(ind + width, cond2_aa, width, facecolor=color2, label=c2) + for x, y, z in zip(ind, max_valaa, aa_name): + ax.text(x + width, y + 0.2, '%s' % z, ha='center', va='bottom', fontsize=14) + ax.set_ylabel('Ribosome Occupancy (percent of normalized reads)') + ax.set_xlabel('Amino Acid') + handles, labels = ax.get_legend_handles_labels() + ax.legend(handles, labels) + pl.savefig(dirout + '/hist_amino_acid.png', format="png", dpi=340) + pl.clf() + + + # # compute theorical count in COND2 + sum2 = (sum21 + sum22) / 2 + cond2_count = [] + for z in cond1_norm.itervalues() : + count = int(z * sum2 / 100) + cond2_count.append(count) + + expected = array(cond2_count) + observed = array(list(cond2.itervalues())) + + # write result + with open(outfile, 'w') as out : + out.write('Codon,Raw_' + c1 + ',Raw_' + c2 + ',Norm_' + c1 + ',Norm_' + c2 + ',FC(Mut/WT)\n') + for i in codon_sorted: + ## if global foldchange is equal to zero + if cond1_norm[i] == 0 and cond2_norm[i] == 0: + out.write(i + ',' + str(cond1[i]) + ',' + str(cond2[i]) + ',' + str(cond1_norm[i]) + ',' + str(cond2_norm[i]) + ',1.0\n') + elif cond1_norm[i] == 0 : + out.write(i + ',' + str(cond1[i]) + ',' + str(cond2[i]) + ',' + str(cond1_norm[i]) + ',' + str(cond2_norm[i]) + ',0.0\n') + else: + out.write(i + ',' + str(cond1[i]) + ',' + str(cond2[i]) + ',' + str(cond1_norm[i]) + ',' + str(cond2_norm[i]) + ',' + str(cond2_norm[i] / cond1_norm[i]) + '\n') + with errstate(all='ignore'): + chi = stats.chisquare(observed, expected) + out.write('Khi2 test\n') + out.write('T : ' + str(chi[0]) + '; p-value : ' + str(chi[1]) + '\n') + + + + # plot result + fig = pl.figure(figsize=(20,10), num=1) + width = .40 + ind = arange(len(codon_sorted)) + ax = fig.add_subplot(111) + pl.xlim(0, len(codon_sorted) + 1) + ax.spines['right'].set_color('none') + ax.spines['top'].set_color('none') + ax.xaxis.set_ticks([]) + ax.spines['left'].set_smart_bounds(True) + ax.yaxis.set_ticks_position('left') + ax.bar(ind, list(cond1_norm.itervalues()), width, facecolor=color1, yerr=std_cond1, error_kw={'elinewidth':1, 'ecolor':'black'}, label=c1) + ax.bar(ind + width, list(cond2_norm.itervalues()), width, yerr=std_cond2, facecolor=color2, error_kw={'elinewidth':1, 'ecolor':'black'}, label=c2) + for x, y, z in zip(ind, max_val, codon_sorted): + ax.text(x + width, y + 0.2, '%s' % z, ha='center', va='bottom', fontsize=8) + ax.set_ylabel('Ribosome Occupancy (percent of normalized reads)') + ax.set_xlabel('Codons') + handles, labels = ax.get_legend_handles_labels() + ax.legend(handles, labels) + pl.savefig(dirout + '/hist_codons.png', format="png", dpi=340) + pl.clf() + + + elif len(result) == 2 : + + # store each dict in OrderedDict sorted by key to make code more readable + cond1 = result[0] + cond2 = result[1] + cond1_norm = result[0].copy() + cond2_norm = result[1].copy() + # pdb.set_trace() + # get codon order in one of list + codon_sorted = sorted(cond1.iterkeys(), reverse=False) + try: + # get sum of each list + sum1 = sum(list(cond1.itervalues())) + sum2 = sum(list(cond2.itervalues())) + # #Normalize values by sum of each libraries + cond1_norm.update ((x, (y / sum1) * 100.0) for x, y in cond1_norm.items()) + cond2_norm.update((x, (y / sum2) * 100.0) for x, y in cond2_norm.items()) + except ZeroDivisionError: + stop_err("Not enough reads to compute the codon occupancy. "+str(sum1)+" and "+str(sum2)+" reads are used for each condition, respectively.\n") + + # # compute theorical count in COND2 + cond2_count = [] + for z in cond1_norm.itervalues() : + count = int(z * sum2 / 100.0) + cond2_count.append(count) + + expected = array(cond2_count) + observed = array(list(cond2.itervalues())) + + AA = get_aa_dict(cond1_norm, cond2_norm) + + max_val = [] + cond1_aa = [] + cond2_aa = [] + aa_name = list(AA.iterkeys()) + for z in AA.itervalues(): + cond1_aa.append(z[0]) + cond2_aa.append(z[1]) + max_val.append(max(z)) + + # # plot amino acid profile : + fig = pl.figure(figsize=(15,10), num=1) + width = .45 + ax = fig.add_subplot(111) + ind = arange(21) + pl.xlim(0, 21) + #kwargs = {"hatch":'x'} + #ax.bar(ind, cond1_aa, width, facecolor=color1, label=c1, **kwargs) + #kwargs = {"hatch":'.'} + #ax.bar(ind + width, cond2_aa, width, facecolor=color2, label=c2, **kwargs) + ax.bar(ind, cond1_aa, width, facecolor=color1, label=c1) + ax.bar(ind + width, cond2_aa, width, facecolor=color2, label=c2) + #for x, y, z in zip(ind, max_val, aa_name): + # ax.text(x + width, y + 0.2, '%s' % z, ha='center', va='bottom', fontsize=14) + axis_font = {'size':'10'} + pl.xticks(ind + width, aa_name,**axis_font) + ax.spines['right'].set_visible(False) + ax.spines['top'].set_visible(False) + ax.yaxis.set_ticks_position('left') + ax.xaxis.set_ticks_position('bottom') + #ax.xaxis.set_ticks([]) + ax.set_ylabel('Ribosome Occupancy (percent of normalized reads)',**axis_font) + ax.set_xlabel('Amino Acids', **axis_font) + handles, labels = ax.get_legend_handles_labels() + font_prop = font_manager.FontProperties(size=8) + ax.legend(handles, labels, prop=font_prop) + pl.savefig(dirout + '/hist_amino_acid.png', format="png", dpi=340) + pl.clf() + + # write result + with open(outfile, 'w') as out : + out.write('Codon,Raw_' + c1 + ',Raw_' + c2 + ',Norm_' + c1 + ',Norm_' + c2 + ',FC(Mut/WT)\n') + for i in codon_sorted: + if cond1_norm[i] == 0 and cond2_norm[i] == 0: + out.write(i + ',' + str(cond1[i]) + ',' + str(cond2[i]) + ',' + str(cond1_norm[i]) + ',' + str(cond2_norm[i]) + ',1.0\n') + elif cond1_norm[i] == 0 : + out.write(i + ',' + str(cond1[i]) + ',' + str(cond2[i]) + ',' + str(cond1_norm[i]) + ',' + str(cond2_norm[i]) + ',0.0\n') + else: + out.write(i + ',' + str(cond1[i]) + ',' + str(cond2[i]) + ',' + str(cond1_norm[i]) + ',' + str(cond2_norm[i]) + ',' + str(cond2_norm[i] / cond1_norm[i]) + '\n') + out.write('Khi2 test\n') + with errstate(all='ignore'): + chi = stats.chisquare(observed, expected) + out.write('T : ' + str(chi[0]) + '; p-value : ' + str(chi[1]) + '\n') + + # # get max value for each codon for histogram + max_val = [] # # max value for graph + for i in cond1: + # # max value for each codon + max_val.append(max(cond1_norm[i], cond2_norm[i])) + + # plot result + fig = pl.figure(figsize=(20,10), num=1) + #fig = pl.figure(num=1) + width = .40 + ind = arange(len(codon_sorted)) + ax = fig.add_subplot(111) + pl.xlim(0, len(codon_sorted) + 1) + ax.spines['right'].set_color('none') + ax.spines['top'].set_color('none') + ax.xaxis.set_ticks([]) + ax.spines['left'].set_smart_bounds(True) + ax.yaxis.set_ticks_position('left') + ax.bar(ind, list(cond1_norm.itervalues()), width, facecolor=color1, label=c1) + ax.bar(ind + width, list(cond2_norm.itervalues()), width, facecolor=color2, label=c2) + for x, y, z in zip(ind, max_val, codon_sorted): + ax.text(x + width, y + 0.2, '%s' % z, ha='center', va='bottom', fontsize=8) + ax.set_ylabel('Ribosome Occupancy (percent of normalized reads)') + ax.set_xlabel('Codons') + handles, labels = ax.get_legend_handles_labels() + ax.legend(handles, labels) + pl.savefig(dirout + '/hist_codons.png', format="png", dpi=340) + pl.clf() + + + else : + stop_err('Error running codon usage plotting : ' + str(e)) + + + return (cond1_norm, cond2_norm, chi[1]) + +def write_html_file(html, chi_pval, cond1, cond2): + try : + + + html_str = """ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> + +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> + <link href="/static/june_2007_style/blue/base.css" media="screen" rel="Stylesheet" type="text/css" /> + </head> + <body> + <h3>Global visualization</h3> + <p> + <h5>Visualization of density footprint in each codon.</h5><br> If user has selected "Yes" for the replicate option the standard deviation between each replicate is plotted as an error bar in histogram.<br> + <img border="0" src="hist_codons.png" width="1040"/> + </p> + <p> + <h5>Test for homogeneity distribution between each condition</h5><br> + H0 : %s and %s are same distribution <br> + Khi2 test p-value: %s<br><br> + If p-value less than 0.05, we can reject homogeneity distribution so we can hypothesize that distributions are not the same. Otherwise, we accept H0<br> + + </p> + <p> + <h5>Visualization of density footprint in each codon groupe by amino acid</h5><br> + <img border="0" src="hist_amino_acid.png" width="1040"/> + </p> + </body> +</html> """ % (cond1,cond2,chi_pval) + + + html_file = open(html, "w") + html_file.write(html_str) + html_file.close() + + except Exception, e : + stop_err('Error during html page creation : ' + str(e)) + + + + +def check_codons_list (codons) : + + for codon in codons : + if codon not in init_codon_dict().iterkeys() : + stop_err('Please to enter a valid codon : ' + codon + ' is not find\n') + + +def check_index_bam (bamfile) : + # #testing indexed bam file + if os.path.isfile(bamfile + ".bai") : + pass + else : + cmd = "samtools index %s " % (bamfile) + proc = subprocess.Popen(args=cmd, shell=True, stderr=subprocess.PIPE) + returncode = proc.wait() + # if returncode != 0: + # raise Exception + +def plot_fc (cond1, cond2, site, dirout): + + fc = cond1.copy() + + for key, value in fc.iteritems(): + if cond1[key] == 0: + fc[key] = 1 + else: + fc[key] = cond2[key]/cond1[key] + + index = arange(len(fc.keys())) + label = fc.keys() + label = [w.replace('T','U') for w in label] + pl.figure(figsize=(15,10), num=1) + ax = pl.subplot(1,1,1) + pl.xticks([]) + pl.scatter(index, fc.values(), color='b') + pl.axhline(y=1,color='r') + pl.xticks(index, label, rotation=90) + pl.ylabel('Foldchange of codon occupancy') + ax.yaxis.set_ticks_position('left') + ax.xaxis.set_ticks_position('bottom') + pl.ylim(-1,3) + pl.title(site+" site") + pl.savefig(dirout + '/fc_codons.png', format="png", dpi=340) + + +def __main__(): + + + # Parse command line options + parser = optparse.OptionParser() + parser.add_option("-g", "--gff", dest="gff", type="string", + help="gff file", metavar="FILE") + + parser.add_option("-1", "--file1", dest="file1", type="string", + help="Bam Ribo-Seq alignments cond 1, if rep option, separate files by commas ", metavar="FILE") + + parser.add_option("-2", "--file2", dest="file2", type="string", + help="Bam Ribo-Seq alignments cond 2, if rep option, separate files by commas", metavar="FILE") + + parser.add_option("-c", "--cond1", dest="c1", type="string", + help="Name for first condition", metavar="STR") + + parser.add_option("-C", "--cond2", dest="c2", type="string", + help="Name of second condition", metavar="STR") + + parser.add_option("-k", "--kmer", dest="kmer", type="int", default = 28 , + help="Length of your phasing reads", metavar="INT") + +# parser.add_option("-l", "--list", dest="list_cod", type= "string", +# help="list of codons to compare to other", metavar="STR") + + parser.add_option("-o", "--out", dest="outfile", type="string", + help="write report to FILE", metavar="FILE") + + parser.add_option("-d", "--dirout", dest="dirout", type="string", + help="write report to PNG files", metavar="FILE") + + parser.add_option("-a", "--asite", dest="asite", type="int", default = 15 , + help="Off-set from the 5'end of the footprint to the A-site (default is 15)", metavar="INT") + + parser.add_option("-s", "--site", dest="site", type="string", default = "A" , + help="Script can compute in site A, P or E (default is A-site)", metavar="A|P|E") + + parser.add_option("-r", "--rep", dest="rep", type="string", default = "no" , + help="if replicate or not", metavar="yes|no") + + parser.add_option("-x", "--hex_col1", dest="color1", type= "string", default = "SkyBlue" , + help="Color for first condition", metavar="STR") + + parser.add_option("-X", "--hex_col2", dest="color2", type= "string", default = "Plum" , + help="Color for second condition", metavar="STR") + + parser.add_option("-q", "--quiet", + action="store_false", dest="verbose", default=True, + help="don't print status messages to stdout") + + (options, args) = parser.parse_args() + print "Begin codon frequency analysis at", time.asctime(time.localtime(time.time())) + + try: + authorized_site = ["A", "P", "E"] + if options.site not in authorized_site : + stop_err(options.site + ' is not a authorized ribosome site') + + ## Check if colors exist + if not colors.is_color_like(options.color1) : + stop_err( options.color1+' is not a proper color' ) + if not colors.is_color_like(options.color2) : + stop_err( options.color2+' is not a proper color' ) + + + #### NOT USE IN FINAL VERSION + # # get codon list + # codons = options.list_cod.upper().split(',') + # check_codons_list(codons) + GFF = HTSeq.GFF_Reader(options.gff) + # # get html file and directory : + (html, html_dir) = options.dirout.split(',') + if not os.path.exists(html_dir): + try: + os.mkdir(html_dir) + except Exception, e : + stop_err('Error running make directory : ' + str(e)) + # #RUN analysis + # #If there are replicats + if options.rep == "yes" : + result = [] + # split name of each file options by "," + cond1 = options.file1.split(',') + cond2 = options.file2.split(',') + # # calcul for each file + for fh in itertools.chain(cond1, cond2): + check_index_bam (fh) + result.append(get_codon_usage(fh, GFF, options.site, options.kmer, options.asite)) + (cond1, cond2, chi_pval) = plot_codon_usage(result, html_dir, options.c1, options.c2, options.outfile,options.color1, options.color2) + # t_pval = compute_FC_plot(cond1,cond2,codons,html_dir) + plot_fc (cond1, cond2, options.site, html_dir) + + # #If there are no replicat + elif options.rep == "no" : + result = [] + # #calcul for each cond + for fh in (options.file1, options.file2): + check_index_bam (fh) + result.append(get_codon_usage(fh, GFF, options.site, options.kmer,options.asite)) + (cond1, cond2, chi_pval) = plot_codon_usage(result, html_dir, options.c1, options.c2, options.outfile,options.color1, options.color2) + + # t_pval = compute_FC_plot(cond1,cond2,codons,html_dir) + plot_fc (cond1, cond2, options.site, html_dir) + else : + sys.stderr.write("Please enter yes or no for --rep option. Programme aborted at %s" % time.asctime(time.localtime(time.time()))) + sys.exit() + + # write_html_file(html,chi_pval,t_pval,codons,options.c1, options.c2) + write_html_file(html, chi_pval, options.c1, options.c2) + + print "Finish codon frequency analysis at", time.asctime(time.localtime(time.time())) + except Exception, e: + stop_err('Error running codon frequency analysis (main program) : ' + str(e)) + + +if __name__=="__main__": + __main__()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ribo_tools/get_codon_frequency.xml Wed Sep 27 04:59:10 2017 -0400 @@ -0,0 +1,101 @@ +<tool id="Codon_analysis" name="Codon_density"> + <description>To compare Ribo-seq alignments between two sets of conditions, to determine codon occupancy</description> + <requirements> + <requirement type="package">samtools</requirement> + <requirement type="python-module">matplotlib</requirement> + <requirement type="python-module">numpy</requirement> + <requirement type="python-module">scipy</requirement> + <requirement type="python-module">Bio</requirement> + </requirements> + <command interpreter="python"> + get_codon_frequency.py + --gff=$annot + --rep=$replicat_opt['rep'] + --cond1 $cond1 + --cond2 $cond2 + --kmer=$kmer + --asite=$asite + --out $out + --site $site + --hex_col1 $color1 + --hex_col2 $color2 + --dirout $html_file,$html_file.files_path + #if str( $replicat_opt['rep'] ) == 'yes': + --file1=$file1,$file11 + --file2=$file2,$file22 + #else + --file1=$file1 + --file2=$file2 + #end if + + + </command> + + <inputs> + <param name="annot" type="data" label="Reference annotation file (GFF)" format="gff" /> + <conditional name="replicat_opt"> + <param name="rep" type="select" label="Replicate option"> + <option value="yes">Yes (only two replicates by condition)</option> + <option value="no">No</option> + </param> + <when value="yes"> + ## Use conditional balise : if rep =yes : 4 files, else 2 files + <param name="file1" type="data" label="First replicate of first condition (Bam file)" format="bam" /> + <param name="file11" type="data" label="Second replicate of first condition (Bam file)" format="bam" /> + <param name="file2" type="data" label="First replicate of second condition (Bam file)" format="bam" /> + <param name="file22" type="data" label="First replicate of second condition (Bam file)" format="bam" /> + </when> + <when value="no"> + <param name="file1" type="data" label="First Bam file" format="bam" /> + <param name="file2" type="data" label="Second Bam File" format="bam" /> + </when> + </conditional> + <param name="site" type="select" label="Please choose a ribosomal site for codon analysis"> + <option value="A">A</option> + <option value="P">P</option> + <option value="E">E</option> + </param> + <param name="asite" type="integer" label="Off-set from the 5'end of the footprint to the A-site" value ="15" /> + <param name="kmer" type="integer" label="Lenght of the best phasing footprints" value ="28" /> + <param name="cond1" type="text" size="25" label="Condition one" help="Required even if no replicate" value="cond1"/> + <param name="cond2" type="text" size="25" label="Condition two" help="Required even if no replicate" value="cond2"/> + <param name="color1" type="text" size="50" label="Color for first condition" value ="SkyBlue" help="Enter standard name, hex color string, or rgb code. You can find all colors here : http://pythonhosted.org/ete2/reference/reference_svgcolors.html" /> + <param name="color2" type="text" size="50" label="Color for second condition" value ="Plum" help="Enter standard name, hex color string, or rgb code. You can find all colors here : http://pythonhosted.org/ete2/reference/reference_svgcolors.html" /> + + </inputs> + + <outputs> + <data format="csv" name="out" label="Codon analysis output file on ${on_string}"/> + <data format="html" name="html_file" label="Codon analysis results on ${on_string}"/> + + </outputs> + + <help> + +Summary +------- +This tool uses Ribo-seq (BAM file) to determine whether codon occupancy differs between two sets of conditions. For each footprint, the codons at the chosen site are recorded and a histogram displaying all the normalised codon numbers is plotted for both sets of conditions. A second histogram groups together all the codons corresponding to a given amino acid. A chi-squared test is then carried out to determine whether the distribution of the codons used is the same in both sets of conditions. + + +Output +------- +This tool provides an html file containing graphs and a statistical test result. An additional csv file with codon numbers is provided. + + +Dependances +------------ + +.. class:: warningmark + +This tool depends on Python (>=2.7) and following packages : numpy 1.8.0, Biopython 1.58, scipy 0.12.0 and matplotlib 1.3.1. Samtools is used for bam manipulation. + + + + + + + + + + </help> +</tool>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ribo_tools/kmer_analysis.py Wed Sep 27 04:59:10 2017 -0400 @@ -0,0 +1,464 @@ +#!/usr/bin/env python2.7.3 +#-*- coding: utf-8 -*- +''' + Created on Jun. 2014 + @author: rachel legendre + @copyright: rachel.legendre@igmors.u-psud.fr + @license: GPL v3 +''' + +import os, sys, time, optparse, re, urllib, subprocess, tempfile, commands +import pysam +#from matplotlib import pyplot as pl +import matplotlib +matplotlib.use('Agg') +import matplotlib.pyplot as pl +from numpy import arange +from collections import OrderedDict +import ribo_functions +#import cPickle +## suppress matplotlib warnings +import warnings +warnings.filterwarnings('ignore') + + +total_mapped_read = 0 + + +def stop_err( msg ): + sys.stderr.write( "%s\n" % msg ) + sys.stderr.write( "Programme aborted at %s\n" % time.asctime( time.localtime( time.time() ) ) ) + sys.exit() + + +def split_bam(bamfile,tmpdir): + ''' + split bam by chromosome and write sam file in tmpdir + ''' + try: + #get header + results = subprocess.check_output(['samtools', 'view', '-H',bamfile]) + header = results.split('\n') + + #define genome size + genome = [] + for line in header: + result = re.search('SN', line) + if result : + #print line + feat = line.split('\t') + chrom = re.split(":", feat[1]) + #print feat[1] + genome.append(chrom[1]) + + #split sam by chrom + n = 0 + for chrm in genome: + with open(tmpdir+'/'+chrm+'.sam', 'w') as f : + #write header correctly for each chromosome + f.write(header[0]+'\n') + expr = re.compile(chrm+'\t') + el =[elem for elem in header if expr.search(elem)][0] + f.write(el+'\n') + f.write(header[-2]+'\n') + #write all reads for each chromosome + reads = subprocess.check_output(["samtools", "view", bamfile, chrm]) + f.write(reads) + # calculate number of reads + n += reads.count(chrm) + + sys.stdout.write("%d reads are presents in your bam file\n" % n) + + except Exception, e: + stop_err( 'Error during bam file splitting : ' + str( e ) ) + + + +def get_first_base(tmpdir, kmer): + ''' + write footprint coverage file for each sam file in tmpdir and get kmer distribution + ''' + global total_mapped_read + + ## tags by default + multi_tag = "XS:i:" + tag = "IH:i:1" + + ## init kmer dict + KMER = OrderedDict({}) + + try: + file_array = (commands.getoutput('ls '+tmpdir)).split('\n') + ##write coverage for each sam file in tmpdir + for samfile in file_array: + with open(tmpdir+'/'+samfile, 'r') as sam : + ##get chromosome name + chrom = samfile.split(".sam")[0] + + for line in sam: + #initialize dictionnary + if '@SQ\tSN:' in line : + size = int(line.split('LN:')[1]) + genomeF = [0]*size + genomeR = [0]*size + # define multiple reads keys from mapper + elif '@PG\tID' in line : + if 'bowtie' in line: + multi_tag = "XS:i:" + elif 'bwa' in line: + multi_tag = "XT:A:R" + elif 'TopHat' in line: + tag = "NH:i:1" + else : + stop_err("No PG tag find in "+samfile+". Please use bowtie, bwa or Tophat for mapping") + + # get footprint + elif re.search('^[^@].+', line) : + len_read = len(line.split('\t')[9]) + ##full kmer dict + if KMER.has_key(len_read): + KMER[len_read] += 1 + else : + KMER[len_read] = 1 + + #print line.rstrip() + read_pos = int(line.split('\t')[3]) + read_sens = int(line.split('\t')[1]) + #len_read = len(line.split('\t')[9]) + if len_read == kmer and (tag in line or multi_tag not in line): + ###if line.split('\t')[5] == '28M' : + total_mapped_read +=1 + #if it's a forward read + if read_sens == 0 : + #get 5' base + genomeF[read_pos] += 1 + #if it's a reverse read + elif read_sens == 16 : + #get 5' base + read_pos += (len_read-1) + genomeR[read_pos] += 1 + + #try: + #write coverage in files + with open(tmpdir+'/assoCov_'+chrom+'.txt', 'w') as cov : + for i in range(0,size): + cov.write(str(genomeF[i])+'\t-'+str(genomeR[i])+'\n') + #except Exception,e: + # stop_err( 'Error during coverage file writting : ' + str( e ) ) + + #sys.stdout.write("%d reads are in your bam file\n" % total_mapped_read) + + return KMER + + except Exception, e: + stop_err( 'Error during footprinting : ' + str( e ) ) + + +def __percent__(prop): + + if sum(prop) != 0 : + perc = [0,0,0] + if prop[0] != 0 : + perc[0] = int("{0:.0f}".format((prop[0]*100.0)/sum(prop))) + if prop[1] != 0 : + perc[1] = int("{0:.0f}".format((prop[1]*100.0)/sum(prop))) + if prop[2] != 0 : + perc[2] = int("{0:.0f}".format((prop[2]*100.0)/sum(prop))) + return perc + else : + return prop + + +def frame_analysis(tmpdir,GFF): + ''' + This function take footprint and annotation (gff) for analyse reading frame in each gene + ''' + global total_mapped_read + try: + chrom = "" # initializing chromosome + nb_gene = 0 # number of analysed genes + whole_phasing = [0,0,0] + for gene in GFF['order']: + ## maybe no start position in GTF file so we must to check and replace + exon_number = GFF[gene]['exon_number'] + try : GFF[gene]['start'] + except : + if GFF[gene]['strand'] == '+' : + GFF[gene]['start'] = GFF[gene]['exon'][1]['start'] + else : + GFF[gene]['start'] = GFF[gene]['exon'][exon_number]['stop'] + ## also for stop coordinates + try : GFF[gene]['stop'] + except : + if GFF[gene]['strand'] == '+' : + GFF[gene]['stop'] = GFF[gene]['exon'][exon_number]['stop'] + + else : + GFF[gene]['stop'] = GFF[gene]['exon'][1]['start'] + cov = [] + ##first chromosome : we open corresponding file + try: + if chrom == "" : + chrom = GFF[gene]['chrom'] + with open(tmpdir+"/assoCov_"+chrom+".txt") as f : + data = f.readlines() + ##if we change chromosome + elif chrom != GFF[gene]['chrom'] : + chrom = GFF[gene]['chrom'] + with open(tmpdir+"/assoCov_"+chrom+".txt") as f : + data = f.readlines() + except IOError : + print tmpdir+"/assoCov_"+chrom+".txt doesn't exist" + + try: + ## if a gene without intron : + if GFF[gene]['exon_number'] == 1: + + ## get coverage for each gene + if GFF[gene]['strand'] == "+": + for i in range(GFF[gene]['exon'][1]['start'],GFF[gene]['exon'][1]['stop']+1): + cov.append(int((data[i].rstrip()).split("\t")[0])) + else : + for i in range(GFF[gene]['exon'][1]['start'],GFF[gene]['exon'][1]['stop']+1): + cov.append(int(((data[i].rstrip()).split("\t")[1]).replace("-",""))) + cov.reverse() + else : + ## For each gene, get coverage and sum of exon size + if GFF[gene]['strand'] == "+": + + for exon in range(1,GFF[gene]['exon_number']+1) : + for i in range(GFF[gene]['exon'][exon]['start'],GFF[gene]['exon'][exon]['stop']+1): + #if i <= GFF[gene]['stop'] : + cov.append(int((data[i].rstrip()).split("\t")[0])) + else : + + for exon in range(1,GFF[gene]['exon_number']+1) : + for i in range(GFF[gene]['exon'][exon]['start'],GFF[gene]['exon'][exon]['stop']+1): + #if i <= GFF[gene]['start'] : + cov.append(int(((data[i].rstrip()).split("\t")[1]).replace("-",""))) + cov.reverse() + except : + #print gene+" could not be analysed." + #del GFF[gene] + continue + len_cov = len(cov) + prop = [0,0,0] + for nuc in range(0,len_cov-2,3) : + ## Calculate proportion + prop[0] += cov[nuc] + prop[1] += cov[nuc+1] + prop[2] += cov[nuc+2] + whole_phasing = (map(lambda (x, y): x + y, zip(whole_phasing, prop))) + + whole_phasing = __percent__(whole_phasing) + #sys.stdout.write("Proportion of reads in each frame :\n%s\n" % whole_phasing) + return whole_phasing + + except Exception, e: + stop_err( 'Error during frame analysis : ' + str( e ) ) + + + +def make_report(html_file, dirout, kmer, results) : + + array = sorted(kmer.items(), key=lambda x: x[0]) + values = [] + label = [] + for x,y in array : + values.append(y) + label.append(x) + index = arange(len(label)) + bar_width = 0.35 + axis_font = {'size':'10'} + fig, ax = pl.subplots() + pl.bar(index ,values, color='LightsteelBlue') + pl.xlabel('kmer value', **axis_font) + pl.ylabel('Number of reads', **axis_font) + pl.title('Number of reads for each k-mer') + pl.xticks(index + bar_width, label, **axis_font) + #pl.show() + fig.subplots_adjust() + pl.savefig(dirout+"/kmer_proportion.png", format='png', dpi=640) + pl.clf() + + for key, phase in results.iteritems() : + fig = pl.figure(num=1) + frame = phase + index = arange(3) + bar_width = 0.5 + pl.bar(index,frame,color=['RoyalBlue','LightSkyBlue','LightBlue']) + pl.xlabel('Frame in gene', **axis_font) + pl.ylabel('Percent of read', **axis_font) + pl.title('Proportion of reads in each frame for '+str(key)+'-mer') + pl.xticks(index+bar_width, ('1', '2', '3'), **axis_font) + #pl.tight_layout() + pl.ylim(0,100) + fig.subplots_adjust() + pl.draw() + pl.show() + pl.savefig(dirout+"/"+str(key)+"_phasing.png", format='png', dpi=300) + pl.clf() + + kmer_summary = '' + kmer_sorted = OrderedDict(sorted(kmer.iteritems(), key=lambda x: x[0])) + for key, number in kmer_sorted.iteritems() : + if number > 100 : + kmer_summary += '<li>' + kmer_summary += 'Analysis of '+str(key)+'-mer : <br>' + kmer_summary += 'You have '+str(number)+' '+str(key)+'-mer : <br>' + kmer_summary += 'Phasing of '+str(key)+'-mer is : '+str(results[key])+'<br>' + kmer_summary += '<img border="0" src="'+str(key)+'_phasing.png" width="50%%" height="50%%"/><br>' + kmer_summary += '</li>' + else : + kmer_summary += '<li>' + kmer_summary += 'Analysis of '+str(key)+'-mer : <br>' + kmer_summary += 'You have '+str(number)+' '+str(key)+'-mer : <br>' + kmer_summary += '</li>' + + html_str = """ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> + +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <link href="lightbox/css/lightbox.css" rel="stylesheet" /> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> + <title>kmer analysis</title> + </head> + <body> + <h1>k-mer analysis results</h1> + <p> + <ul> + <li> + Number of reads for each k-mer in your sample : <br /> + <img border="0" src="kmer_proportion.png" width="50%%" height="50%%"/> + </li> + %s + </ul> + </p> + </body> +</html> +""" + all = html_str % kmer_summary + + html = open(html_file,"w") + html.write(all) + html.close() + +def __main__(): + + #Parse command line options + parser = optparse.OptionParser() + parser.add_option("-g", "--gff", dest="gff", type= "string", + help="GFF annotation file", metavar="FILE") + + parser.add_option("-b", "--bam", dest="bamfile", type= "string", + help="Bam Ribo-Seq alignments ", metavar="FILE") + + parser.add_option("-o", "--out", dest="html_file", type= "string", + help="write report to FILE", metavar="FILE") + + parser.add_option("-d", "--dirout", dest="dirout", type= "string", + help="write report to PNG files", metavar="FILE") + + parser.add_option("-q", "--quiet", + action="store_false", dest="verbose", default=True, + help="don't print status messages to stdout") + + (options, args) = parser.parse_args() + sys.stdout.write("Begin kmer and phasing analysis at %s\n" % time.asctime( time.localtime( time.time() ) ) ) + + try: + + if not os.path.exists(options.dirout): + try: + os.mkdir(options.dirout) + except Exception, e : + stop_err('Error running make directory : ' + str(e)) + + ##testing indexed bam file + if os.path.isfile(options.bamfile+".bai") : + pass + else : + cmd = "samtools index %s " % (options.bamfile) + proc = subprocess.Popen( args=cmd, shell=True, stderr = subprocess.PIPE) + returncode = proc.wait() + + tmpdir = tempfile.mkdtemp() + ## identify GFF or GTF format from 9th column + with open (options.gff,"r") as gffile : + for line in gffile : + if '#' in line : + ## skip header + gffile.next() + elif 'gene_id' in line : + ## launch gtf reader : + GFF = ribo_functions.store_gtf(options.gff) + break + elif 'ID=' in line : + ## launch gff reader + GFF = ribo_functions.store_gff(options.gff) + break + else : + stop_err( 'Please check your annotation file is in correct format, GFF or GTF' ) + #GFF = store_gff(options.gff) + #GFF = ribo_functions.store_gtf(options.gff) + ## check gff reading + if not GFF['order'] : + stop_err( 'Incorrect GFF file' ) + for gene in GFF['order']: + if not GFF[gene]['exon'] : + del GFF[gene] + GFF['order'].remove(gene) + ## split bam + split_bam(options.bamfile,tmpdir) + ################################### + ## First analysis with 28mer : + ################################### + ## compute coverage and distribution kmer + kmer = get_first_base(tmpdir, 28) + ## compute phasing + whole_phasing = frame_analysis(tmpdir,GFF) + ## save phasing + results = {} + results[28] = whole_phasing + ## compute analysis with other kmer + for keys in kmer.iterkeys() : + if keys != 28 : + + ## If not enought reads in this kmer : + if kmer[keys] > 100 : + ## remove all txt files in tmp directory + if os.system("rm "+tmpdir+"/*.txt") != 0 : + stop_err( 'Error during tmp directory cleaning') + ## compute coverage and distribution kmer + tmp = get_first_base(tmpdir, keys) + ## compute phasing + whole_phasing = frame_analysis(tmpdir,GFF) + + results[keys] = whole_phasing + ## get report + make_report(options.html_file, options.dirout, kmer, results) + #======================================================================= + # ############ + # # tests + # ############ + # with open("kmer_dict",'rb') as km: + # kmer = cPickle.load(km) + # with open("results_dict",'rb') as res: + # results = cPickle.load(res) + # with open("kmer_dict",'wb') as km: + # cPickle.dump(kmer,km) + # with open("results_dict",'wb') as res: + # cPickle.dump(results,res) + # make_report(options.html_file, options.dirout, kmer, results) + #======================================================================= + + sys.stdout.write("Finish kmer and phasing analysis at %s\n" % time.asctime( time.localtime( time.time() ) ) ) + + except Exception, e: + stop_err( 'Error running kmer and phasing analysis : ' + str( e ) ) + + +if __name__=="__main__": + __main__() \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ribo_tools/kmer_analysis.xml Wed Sep 27 04:59:10 2017 -0400 @@ -0,0 +1,46 @@ +<tool id="kmer_analysis" name="Kmer"> + <description>To calculate the proportion and phasing of each kmer</description> + <requirements> + <requirement type="package">samtools</requirement> + <requirement type="package">numpy</requirement> + <requirement type="package">matplotlib</requirement> + <requirement type="package">pysam</requirement> + </requirements> + <command interpreter="python"> + kmer_analysis.py --gff $gff --bam $bamfile --dirout $output.files_path --out $output + + </command> + + <inputs> + <param name="gff" type="data" label="Reference annotation file (GFF))" format="gff" /> + <param name="bamfile" type="data" label="Bam file" format="bam" /> + </inputs> + + <outputs> + <data format="html" name="output" label="Kmer report on ${on_string}"/> + + </outputs> + + <help> + +Summary +------- +The kmer tool computes the distribution of footprints length from Bam file and determines the proportion of footprints beginning in each frame, for all annotated genes in the `GFF3`_ file. + + +.. _GFF3: http://gmod.org/wiki/GFF3 + +Output +------- +This tool provides an html report detailing the proportions and phasing of the kmers. + + +Dependances +------------ + +.. class:: warningmark + +This tool depends on Python (>=2.7) and following packages : numpy 1.8.0 and matplotlib 1.3.1. Samtools and pysam are used for bam manipulation. + + </help> +</tool> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ribo_tools/lightbox/README.markdown Wed Sep 27 04:59:10 2017 -0400 @@ -0,0 +1,15 @@ +## Lightbox2 +by Lokesh Dhakar | [lokeshdhakar.com](http://www.lokeshdhakar.com) | [twitter.com/lokesh](http://twitter.com/lokesh) + +### Information and support +For examples, downloads, and information on using Lightbox, visit the Lightbox2 homepage: +[http://lokeshdhakar.com/projects/lightbox2/](http://lokeshdhakar.com/projects/lightbox2/) + +For personal support issues and feature requests, visit the Lightbox forums: +[http://lokeshdhakar.com/forums/](http://lokeshdhakar.com/forums/) + +### License +Licensed under the Creative Commons Attribution 2.5 License - http://creativecommons.org/licenses/by/2.5/ + +* Free for use in both personal and commercial projects. +* Attribution requires leaving author name, author homepage link, and the license info intact.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ribo_tools/lightbox/css/lightbox.css Wed Sep 27 04:59:10 2017 -0400 @@ -0,0 +1,202 @@ +/* line 7, ../sass/lightbox.sass */ +body:after { + content: url(../img/close.png) url(../img/loading.gif) url(../img/prev.png) url(../img/next.png); + display: none; +} + +/* line 11, ../sass/lightbox.sass */ +.lightboxOverlay { + position: absolute; + top: 0; + left: 0; + z-index: 9999; + background-color: black; + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=80); + opacity: 0.8; + display: none; +} + +/* line 20, ../sass/lightbox.sass */ +.lightbox { + position: absolute; + left: 0; + width: 100%; + z-index: 10000; + text-align: center; + line-height: 0; + font-weight: normal; +} +/* line 28, ../sass/lightbox.sass */ +.lightbox .lb-image { + display: block; + height: auto; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + -ms-border-radius: 3px; + -o-border-radius: 3px; + border-radius: 3px; +} +/* line 32, ../sass/lightbox.sass */ +.lightbox a img { + border: none; +} + +/* line 35, ../sass/lightbox.sass */ +.lb-outerContainer { + position: relative; + background-color: white; + *zoom: 1; + width: 250px; + height: 250px; + margin: 0 auto; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; +} +/* line 38, ../../../../.rvm/gems/ruby-1.9.3-p392/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/utilities/general/_clearfix.scss */ +.lb-outerContainer:after { + content: ""; + display: table; + clear: both; +} + +/* line 44, ../sass/lightbox.sass */ +.lb-container { + padding: 4px; +} + +/* line 47, ../sass/lightbox.sass */ +.lb-loader { + position: absolute; + top: 43%; + left: 0%; + height: 25%; + width: 100%; + text-align: center; + line-height: 0; +} + +/* line 56, ../sass/lightbox.sass */ +.lb-cancel { + display: block; + width: 32px; + height: 32px; + margin: 0 auto; + background: url(../img/loading.gif) no-repeat; +} + +/* line 63, ../sass/lightbox.sass */ +.lb-nav { + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 100%; + z-index: 10; +} + +/* line 71, ../sass/lightbox.sass */ +.lb-container > .nav { + left: 0; +} + +/* line 74, ../sass/lightbox.sass */ +.lb-nav a { + outline: none; +} + +/* line 77, ../sass/lightbox.sass */ +.lb-prev, .lb-next { + width: 49%; + height: 100%; + cursor: pointer; + /* Trick IE into showing hover */ + display: block; +} + +/* line 84, ../sass/lightbox.sass */ +.lb-prev { + left: 0; + float: left; +} +/* line 87, ../sass/lightbox.sass */ +.lb-prev:hover { + background: url(../img/prev.png) left 48% no-repeat; +} + +/* line 90, ../sass/lightbox.sass */ +.lb-next { + right: 0; + float: right; +} +/* line 93, ../sass/lightbox.sass */ +.lb-next:hover { + background: url(../img/next.png) right 48% no-repeat; +} + +/* line 96, ../sass/lightbox.sass */ +.lb-dataContainer { + margin: 0 auto; + padding-top: 5px; + *zoom: 1; + width: 100%; + -moz-border-radius-bottomleft: 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + -moz-border-radius-bottomright: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; +} +/* line 38, ../../../../.rvm/gems/ruby-1.9.3-p392/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/utilities/general/_clearfix.scss */ +.lb-dataContainer:after { + content: ""; + display: table; + clear: both; +} + +/* line 103, ../sass/lightbox.sass */ +.lb-data { + padding: 0 4px; + color: #bbbbbb; +} +/* line 106, ../sass/lightbox.sass */ +.lb-data .lb-details { + width: 85%; + float: left; + text-align: left; + line-height: 1.1em; +} +/* line 111, ../sass/lightbox.sass */ +.lb-data .lb-caption { + font-size: 13px; + font-weight: bold; + line-height: 1em; +} +/* line 115, ../sass/lightbox.sass */ +.lb-data .lb-number { + display: block; + clear: left; + padding-bottom: 1em; + font-size: 12px; + color: #999999; +} +/* line 121, ../sass/lightbox.sass */ +.lb-data .lb-close { + display: block; + float: right; + width: 30px; + height: 30px; + background: url(../img/close.png) top right no-repeat; + text-align: right; + outline: none; + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=70); + opacity: 0.7; +} +/* line 130, ../sass/lightbox.sass */ +.lb-data .lb-close:hover { + cursor: pointer; + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); + opacity: 1; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ribo_tools/lightbox/css/screen.css Wed Sep 27 04:59:10 2017 -0400 @@ -0,0 +1,522 @@ +/* line 17, ../../../../.rvm/gems/ruby-1.9.3-p392/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font: inherit; + font-size: 100%; + vertical-align: baseline; +} + +/* line 22, ../../../../.rvm/gems/ruby-1.9.3-p392/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ +html { + line-height: 1; +} + +/* line 24, ../../../../.rvm/gems/ruby-1.9.3-p392/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ +ol, ul { + list-style: none; +} + +/* line 26, ../../../../.rvm/gems/ruby-1.9.3-p392/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ +table { + border-collapse: collapse; + border-spacing: 0; +} + +/* line 28, ../../../../.rvm/gems/ruby-1.9.3-p392/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ +caption, th, td { + text-align: left; + font-weight: normal; + vertical-align: middle; +} + +/* line 30, ../../../../.rvm/gems/ruby-1.9.3-p392/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ +q, blockquote { + quotes: none; +} +/* line 103, ../../../../.rvm/gems/ruby-1.9.3-p392/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ +q:before, q:after, blockquote:before, blockquote:after { + content: ""; + content: none; +} + +/* line 32, ../../../../.rvm/gems/ruby-1.9.3-p392/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ +a img { + border: none; +} + +/* line 116, ../../../../.rvm/gems/ruby-1.9.3-p392/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ +article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section, summary { + display: block; +} + +/* Typography + *----------------------------------------------- */ +/* line 10, ../sass/screen.sass */ +body, +input, +textarea { + color: #cccccc; + font-size: 18px; + font-family: "Karla", "lucida grande", sans-serif; +} + +/* line 17, ../sass/screen.sass */ +h1, +h2, +h3, +h4, +h5, +h6 { + line-height: 1.2em; + color: #fdf485; +} + +/* line 26, ../sass/screen.sass */ +h1 { + font-size: 72px; + line-height: 1em; +} + +/* line 30, ../sass/screen.sass */ +h2 { + font-size: 48px; + line-height: 1.2em; + margin-bottom: 0.3em; +} + +/* line 35, ../sass/screen.sass */ +h3 { + font-size: 18px; + text-transform: uppercase; + letter-spacing: 0.1em; + color: #e5d404; + margin-bottom: 0.3em; +} + +/* line 42, ../sass/screen.sass */ +h4 { + font-size: 18px; +} + +/* line 45, ../sass/screen.sass */ +p { + line-height: 1.4em; + margin-bottom: 1em; +} + +/* line 49, ../sass/screen.sass */ +ol { + list-style-type: decimal; +} + +/* line 52, ../sass/screen.sass */ +ul, ol { + margin: 0 0 1.25em 0; +} + +/* line 55, ../sass/screen.sass */ +li { + padding-bottom: 0.8em; + margin-bottom: 0.8em; + border-bottom: 2px solid #515151; + line-height: 1.3em; +} +/* line 60, ../sass/screen.sass */ +li.last-list-item { + border-bottom: none; +} + +/* line 63, ../sass/screen.sass */ +dt { + font-weight: bold; +} + +/* line 66, ../sass/screen.sass */ +dd { + margin-bottom: 1.625em; +} + +/* line 69, ../sass/screen.sass */ +strong { + font-weight: bold; +} + +/* line 72, ../sass/screen.sass */ +i { + font-style: italic; +} + +/* line 75, ../sass/screen.sass */ +pre { + padding: 10px; + margin-top: 5px; + margin-bottom: 10px; + background-color: #2b2b2b; + font: 13px "Andale Mono", "DejaVu Sans Mono", monospace; + line-height: 1.5em; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + overflow-x: auto; +} + +/* line 85, ../sass/screen.sass */ +code, kbd { + padding: 4px; + color: #ac8053; + background-color: #2b2b2b; + font: 13px "Andale Mono", "DejaVu Sans Mono", monospace; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; +} + +/* line 92, ../sass/screen.sass */ +code { + position: relative; + top: -1px; +} + +/* line 96, ../sass/screen.sass */ +pre code { + top: 0; + padding: 0; + background: transparent; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + font-size: 13px; +} + +/* line 103, ../sass/screen.sass */ +a { + color: #00bfa8; + text-decoration: none; +} +/* line 106, ../sass/screen.sass */ +a:hover { + color: #59ffeb; +} + +/* line 109, ../sass/screen.sass */ +::-moz-selection, +::selection { + background: #ff8000; + color: white; +} + +/* line 114, ../sass/screen.sass */ +.sub-point { + display: block; + font-size: 14px; +} +/* line 117, ../sass/screen.sass */ +.sub-point code { + font-size: 12px; + padding: 2px; +} + +/* -- Layout ------------------------------------------------------------------ */ +/* line 124, ../sass/screen.sass */ +body { + margin: 40px 40px 80px 40px; + background: #444444; +} + +/* line 128, ../sass/screen.sass */ +.wrapper { + max-width: 740px; + margin: 0 auto; +} + +/* line 132, ../sass/screen.sass */ +.section { + padding: 0 0 40px 0; + margin-bottom: 40px; + *zoom: 1; + border-bottom: 4px solid #373737; +} +/* line 38, ../../../../.rvm/gems/ruby-1.9.3-p392/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/utilities/general/_clearfix.scss */ +.section:after { + content: ""; + display: table; + clear: both; +} +/* line 137, ../sass/screen.sass */ +.section.last { + border-bottom: none; +} + +/* line 140, ../sass/screen.sass */ +.section-header { + text-align: center; +} + +/* line 143, ../sass/screen.sass */ +.section-subheader { + margin-top: -0.6em; + margin-bottom: 1em; + text-align: center; + font-size: 24px; +} + +/* line 149, ../sass/screen.sass */ +.row { + *zoom: 1; +} +/* line 38, ../../../../.rvm/gems/ruby-1.9.3-p392/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/utilities/general/_clearfix.scss */ +.row:after { + content: ""; + display: table; + clear: both; +} + +/* -- Sections ------------------------------------------------------------------ */ +/* -- Intro -- */ +/* line 156, ../sass/screen.sass */ +.intro-section { + text-align: center; +} + +/* line 159, ../sass/screen.sass */ +.logo { + color: white; + margin-bottom: 0.05em; +} +/* line 162, ../sass/screen.sass */ +.logo .version { + color: #fdf485; +} + +/* line 165, ../sass/screen.sass */ +.author { + margin-top: -9px; + padding-left: 23px; + line-height: 1.2em; +} + +/* line 170, ../sass/screen.sass */ +.author-links { + font-size: 16px; +} + +/* line 173, ../sass/screen.sass */ +.lead { + font-size: 22px; +} + +/* -- Examples -- */ +/* line 177, ../sass/screen.sass */ +.examples-section { + text-align: center; +} + +/* line 180, ../sass/screen.sass */ +.image-row { + *zoom: 1; + margin-bottom: 20px; +} +/* line 38, ../../../../.rvm/gems/ruby-1.9.3-p392/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/utilities/general/_clearfix.scss */ +.image-row:after { + content: ""; + display: table; + clear: both; +} + +/* line 184, ../sass/screen.sass */ +.example-image-link { + display: inline-block; + margin: 0 10px 20px 10px; + line-height: 0; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + border: 4px solid #5e5e5e; + -webkit-transition: all 0.1s ease-out; + -moz-transition: all 0.1s ease-out; + -o-transition: all 0.1s ease-out; + transition: all 0.1s ease-out; +} +/* line 191, ../sass/screen.sass */ +.example-image-link:hover { + border: 4px solid #00bfa8; +} + +/* line 194, ../sass/screen.sass */ +.example-image { + -webkit-border-radius: 2px; + -moz-border-radius: 2px; + -ms-border-radius: 2px; + -o-border-radius: 2px; + border-radius: 2px; +} + +/* -- Download -- */ +/* line 199, ../sass/screen.sass */ +.download-section { + text-align: center; +} + +/* line 202, ../sass/screen.sass */ +.download-button { + display: block; + max-width: 300px; + margin: 0 auto 20px auto; + padding-top: 20px; + padding-bottom: 10px; + background-color: #2b2b2b; + border: 4px solid #444444; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + -webkit-transition: all 0.1s ease-out; + -moz-transition: all 0.1s ease-out; + -o-transition: all 0.1s ease-out; + transition: all 0.1s ease-out; + *zoom: 1; +} +/* line 38, ../../../../.rvm/gems/ruby-1.9.3-p392/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/utilities/general/_clearfix.scss */ +.download-button:after { + content: ""; + display: table; + clear: both; +} +/* line 213, ../sass/screen.sass */ +.download-button:hover { + border-color: #00bfa8; + background-color: #444444; +} +/* line 216, ../sass/screen.sass */ +.download-button .file { + font-size: 36px; + color: white; + line-height: 1em; +} +/* line 220, ../sass/screen.sass */ +.download-button .version { + font-size: 24px; + color: #00bfa8; +} + +/* -- Sharing -- */ +/* line 226, ../sass/screen.sass */ +.sharing { + position: fixed; + top: 20px; + right: 0; +} + +/* -- Donate -- */ +/* line 233, ../sass/screen.sass */ +.donate-button-form { + text-align: center; +} + +/* line 235, ../sass/screen.sass */ +.donate-button { + border: 4px solid rgba(0, 0, 0, 0); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + -webkit-transition: all 0.1s ease-out; + -moz-transition: all 0.1s ease-out; + -o-transition: all 0.1s ease-out; + transition: all 0.1s ease-out; +} +/* line 239, ../sass/screen.sass */ +.donate-button:hover { + background-color: #444444; + border-color: #00bfa8; +} + +/* -- Responsive design -------------------------------------------------------------- */ +@media only screen and (max-width: 640px) { + /* line 247, ../sass/screen.sass */ + body { + margin: 80px 10px 40px 10px; + font-size: 14px; + } + + /* line 250, ../sass/screen.sass */ + h1 { + font-size: 48px; + } + + /* line 252, ../sass/screen.sass */ + h2 { + font-size: 26px; + } + + /* line 254, ../sass/screen.sass */ + h3 { + font-size: 16px; + } + + /* line 256, ../sass/screen.sass */ + ol { + list-style-position: inside; + } + + /* line 258, ../sass/screen.sass */ + code, + kbd, + pre, + pre code { + font-size: 11px; + } + + /* line 263, ../sass/screen.sass */ + .sub-point { + font-size: 12px; + } + /* line 265, ../sass/screen.sass */ + .sub-point code { + font-size: 9px; + } + + /* line 267, ../sass/screen.sass */ + .lead { + font-size: 16px; + } + + /* line 269, ../sass/screen.sass */ + .section { + padding-bottom: 20px; + margin-bottom: 20px; + } + + /* line 272, ../sass/screen.sass */ + .author { + margin-top: -5px; + padding-left: 30px; + } + + /* line 275, ../sass/screen.sass */ + .author-links { + font-size: 12px; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ribo_tools/lightbox/index.html Wed Sep 27 04:59:10 2017 -0400 @@ -0,0 +1,179 @@ +<!DOCTYPE html> +<html lang="en-us"> +<head> + <meta charset="utf-8"> + + <title>Lightbox 2</title> + + <meta name="description" lang="en" content="Lightbox 2 is a script used to overlay images on the current page. It's a snap to setup and works on all modern browsers."/> + <meta name="author" content="Lokesh Dhakar"> + <meta name="viewport" content="width=device-width,initial-scale=1"> + + <script src="js/modernizr.custom.js"></script> + + <link rel="shortcut icon" href="img/demopage/favicon.ico"> + <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Karla:400,700"> + <link rel="stylesheet" href="css/screen.css" media="screen"/> + <link rel="stylesheet" href="css/lightbox.css" media="screen"/> +</head> +<body> + +<div class="wrapper"> + + <section id="intro" class="section intro-section"> + <header> + <h1 class="logo">Lightbox<em class="version">2</em></h1> + </header> + <p class="author"> + by Lokesh Dhakar<br /> + <span class="author-links"> + <a href="https://twitter.com/intent/user?screen_name=lokesh">Follow me on Twitter</a> + </span> + </p> + <p class="lead"> + Lightbox is small javascript library used to overlay images on top of the current page. It's a <em>snap to setup</em> and works on <em>all modern browsers</em>. + </p> + </section> + + <section id="examples" class="section examples-section"> + <header> + <h2 class="section-header">Examples</h2> + </header> + + <h3>Single images</h3> + <div class="image-row"> + <a class="example-image-link" href="img/demopage/image-1.jpg" data-lightbox="example-1"><img class="example-image" src="img/demopage/thumb-1.jpg" alt="thumb-1" width="150" height="150"/></a> + <a class="example-image-link" href="img/demopage/image-2.jpg" data-lightbox="example-2" title="Optional caption."><img class="example-image" src="img/demopage/thumb-2.jpg" alt="thumb-1" width="150" height="150"/></a> + </div> + + <h3 style="clear: both;">An image set</h3> + + <div class="image-row"> + <div class="image-set"> + <a class="example-image-link" href="img/demopage/image-3.jpg" data-lightbox="example-set" title="Click on the right side of the image to move forward."><img class="example-image" src="img/demopage/thumb-3.jpg" alt="Plants: image 1 0f 4 thumb" width="150" height="150"/></a> + <a class="example-image-link" href="img/demopage/image-4.jpg" data-lightbox="example-set" title="Or press the right arrow on your keyboard."><img class="example-image" src="img/demopage/thumb-4.jpg" alt="Plants: image 2 0f 4 thumb" width="150" height="150"/></a> + <a class="example-image-link" href="img/demopage/image-5.jpg" data-lightbox="example-set" title="The script preloads the next image in the set as you're viewing."><img class="example-image" src="img/demopage/thumb-5.jpg" alt="Plants: image 3 0f 4 thumb" width="150" height="150"/></a> + <a class="example-image-link" href="img/demopage/image-6.jpg" data-lightbox="example-set" title="Click anywhere outside the image or the X to the right to close."><img class="example-image" src="img/demopage/thumb-6.jpg" alt="Plants: image 4 0f 4 thumb" width="150" height="150"/></a> + </div> + </div> + </section> + + <section id="download" class="section download-section"> + <header> + <h2 class="section-header">Download</h2> + </header> + + <a href="releases/lightbox2.6.zip" class="download-button"> + <div class="file"> + Lightbox + <div class="version"> + v2.6 + </div> + </div> + </a> + + <p> + Lightbox2 is open-source.<br/>View and fork the code on <a href="https://github.com/lokesh/lightbox2">Github</a>. + </p> + </section> + + <section id="how-to-use" class="section how-to-use-section"> + <header> + <h2 class="section-header">How to use</h2> + <h2 class="section-subheader">Step-by-step instructions</h2> + </header> + <h3>Part 1 - Get setup</h3> + <ol> + <li>Download and unzip the latest version of Lightbox from above.</li> + <li>Look inside the <code>js</code> folder to find <code>jquery-1.10.2.min.js</code> and <code>lightbox-2.6.min.js</code> and load both of these files from your html page. Load jQuery first: +<pre><code><script src="js/jquery-1.10.2.min.js"></script> +<script src="js/lightbox-2.6.min.js"></script> +</code></pre></li> + <li>Look inside the <code>css</code> folder to find <code>lightbox.css</code> and load it from your html page: +<pre><code><link href="css/lightbox.css" rel="stylesheet" /> +</code></pre> + </li> + <li>Look inside the <code>img</code> folder to find <code>close.png</code>, <code>loading.gif</code>, <code>prev.png</code>, and <code>next.png</code>. These files are used in <code>lightbox.css</code>. By default, <code>lightbox.css</code> will look for these images in a folder called <code>img</code>.</li> + </ol> + <h3>Part 2 - Turn it on</h3> + <ul> + <li>Add a <code>data-lightbox</code> attribute to any image link to activate Lightbox. For the value of the attribute, use a unique name for each image. For example: +<pre><code><a href="img/image-1.jpg" data-lightbox="image-1" title="My caption">image #1</a> +</code></pre> + <div class="sub-point"><em>Optional:</em> Set the <code>title</code> attribute if you want to show a caption.</div> </li> + <li>If you have a group of related images that you would like to combine into a set, use the same <code>data-lightbox</code> attribute value for all of the images. For example: +<pre><code><a href="img/image-2.jpg" data-lightbox="roadtrip">image #2</a> +<a href="img/image-3.jpg" data-lightbox="roadtrip">image #3</a> +<a href="img/image-4.jpg" data-lightbox="roadtrip">image #4</a> +</code></pre></li> +<li class="last-list-item"><div class="sub-point">For you long time Lightbox users, don't fret, you can still enable Lightbox by using <code>rel="lightbox"</code>. The new <code>data-lightbox</code> approach is preferred though as it is valid html.</div></li> + </li> + </ul> + </section> + + + <section id="questions" class="section questions-section"> + <header> + <h2 class="section-header">Help</h2> + </header> + <h3>Bugs</h3> + <p>If you find a bug, create an <a href="https://github.com/lokesh/lightbox2/issues">issue on Github</a>. Include your operating system and browser version along with detailed steps to reproduce the bug.</p> + + <h3>Feature requests</h3> + <p>If you want a feature added, please create an <a href="https://github.com/lokesh/lightbox2/issues">issue on Github</a>. Someone else or I might be able to help out. No guarantees.</p> + + <h3>Support questions</h3> + <p>If you have a question that is not a bug or a feature request, your best chance of getting an answer is by following these steps:</p> + <ol class="simple-list"> + <li><a href="http://lokeshdhakar.com/forums">Search the Lightbox forums</a>. <br /><span class="sub-point">You will not be able to post new questions in the forum as posting has been disabled. The forum is having spam problems and the forum will eventually be phased out.</span></li> + <li><a href="http://stackoverflow.com/">Search Stackoverflow</a>.</li> + <li><a href="http://stackoverflow.com/questions/ask">Create a new question on Stackoverflow</a>.</li> + </ol> + </section> + + <section id="doante" class="section donate-section last"> + <header> + <h2 class="section-header">Donate</h2> + </header> + <p> + Lightbox is completely free to use. If you're using Lightbox on a commercial project and are feeling generous, consider a donation. Thanks! + </p> + <form class="donate-button-form" name="_xclick" action="https://www.paypal.com/cgi-bin/webscr" method="post"> + <fieldset> + <input type="hidden" name="cmd" value="_xclick"> + <input type="hidden" name="business" value="lokesh.dhakar@gmail.com"> + <input type="hidden" name="item_name" value="Donation for Lightbox"> + <input type="hidden" name="no_note" value="1"> + <input type="hidden" name="currency_code" value="USD"> + <input type="hidden" name="tax" value="0"> + <input type="hidden" name="bn" value="PP-DonationsBF"> + <input type="image" src="img/demopage/donate.png" name="submit" class="donate-button" alt="Make payments with PayPal - it's fast, free and secure!"> + </fieldset> + </form> + </section> + + <section id="sharing" class="sharing"> + <a href="https://twitter.com/share" class="twitter-share-button" data-via="lokesh" data-size="large">Tweet</a> + <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script> + </section> + + + <script src="js/jquery-1.10.2.min.js"></script> + <script src="js/lightbox-2.6.min.js"></script> + + <script> + var _gaq = _gaq || []; + _gaq.push(['_setAccount', 'UA-2196019-1']); + _gaq.push(['_trackPageview']); + + (function() { + var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; + ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; + var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); + })(); + </script> + +</div> + +</body> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ribo_tools/lightbox/js/jquery-1.10.2.min.js Wed Sep 27 04:59:10 2017 -0400 @@ -0,0 +1,6 @@ +/*! jQuery v1.10.2 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license +//@ sourceMappingURL=jquery-1.10.2.min.map +*/ +(function(e,t){var n,r,i=typeof t,o=e.location,a=e.document,s=a.documentElement,l=e.jQuery,u=e.$,c={},p=[],f="1.10.2",d=p.concat,h=p.push,g=p.slice,m=p.indexOf,y=c.toString,v=c.hasOwnProperty,b=f.trim,x=function(e,t){return new x.fn.init(e,t,r)},w=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=/\S+/g,C=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,k=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,E=/^[\],:{}\s]*$/,S=/(?:^|:|,)(?:\s*\[)+/g,A=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,j=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,D=/^-ms-/,L=/-([\da-z])/gi,H=function(e,t){return t.toUpperCase()},q=function(e){(a.addEventListener||"load"===e.type||"complete"===a.readyState)&&(_(),x.ready())},_=function(){a.addEventListener?(a.removeEventListener("DOMContentLoaded",q,!1),e.removeEventListener("load",q,!1)):(a.detachEvent("onreadystatechange",q),e.detachEvent("onload",q))};x.fn=x.prototype={jquery:f,constructor:x,init:function(e,n,r){var i,o;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof x?n[0]:n,x.merge(this,x.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:a,!0)),k.test(i[1])&&x.isPlainObject(n))for(i in n)x.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(o=a.getElementById(i[2]),o&&o.parentNode){if(o.id!==i[2])return r.find(e);this.length=1,this[0]=o}return this.context=a,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return g.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(g.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},l=1,u=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},l=2),"object"==typeof s||x.isFunction(s)||(s={}),u===l&&(s=this,--l);u>l;l++)if(null!=(o=arguments[l]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(x.isPlainObject(r)||(n=x.isArray(r)))?(n?(n=!1,a=e&&x.isArray(e)?e:[]):a=e&&x.isPlainObject(e)?e:{},s[i]=x.extend(c,a,r)):r!==t&&(s[i]=r));return s},x.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=l),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){if(e===!0?!--x.readyWait:!x.isReady){if(!a.body)return setTimeout(x.ready);x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(a,[x]),x.fn.trigger&&x(a).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray||function(e){return"array"===x.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?c[y.call(e)]||"object":typeof e},isPlainObject:function(e){var n;if(!e||"object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!v.call(e,"constructor")&&!v.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(r){return!1}if(x.support.ownLast)for(n in e)return v.call(e,n);for(n in e);return n===t||v.call(e,n)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||a;var r=k.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=x.buildFragment([e],t,i),i&&x(i).remove(),x.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=x.trim(n),n&&E.test(n.replace(A,"@").replace(j,"]").replace(S,"")))?Function("return "+n)():(x.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||x.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&x.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(D,"ms-").replace(L,H)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:b&&!b.call("\ufeff\u00a0")?function(e){return null==e?"":b.call(e)}:function(e){return null==e?"":(e+"").replace(C,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?x.merge(n,"string"==typeof e?[e]:e):h.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(m)return m.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return d.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),x.isFunction(e)?(r=g.call(arguments,2),i=function(){return e.apply(n||this,r.concat(g.call(arguments)))},i.guid=e.guid=e.guid||x.guid++,i):t},access:function(e,n,r,i,o,a,s){var l=0,u=e.length,c=null==r;if("object"===x.type(r)){o=!0;for(l in r)x.access(e,n,l,r[l],!0,a,s)}else if(i!==t&&(o=!0,x.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(x(e),n)})),n))for(;u>l;l++)n(e[l],r,s?i:i.call(e[l],l,n(e[l],r)));return o?e:c?n.call(e):u?n(e[0],r):a},now:function(){return(new Date).getTime()},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),x.ready.promise=function(t){if(!n)if(n=x.Deferred(),"complete"===a.readyState)setTimeout(x.ready);else if(a.addEventListener)a.addEventListener("DOMContentLoaded",q,!1),e.addEventListener("load",q,!1);else{a.attachEvent("onreadystatechange",q),e.attachEvent("onload",q);var r=!1;try{r=null==e.frameElement&&a.documentElement}catch(i){}r&&r.doScroll&&function o(){if(!x.isReady){try{r.doScroll("left")}catch(e){return setTimeout(o,50)}_(),x.ready()}}()}return n.promise(t)},x.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){c["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=x(a),function(e,t){var n,r,i,o,a,s,l,u,c,p,f,d,h,g,m,y,v,b="sizzle"+-new Date,w=e.document,T=0,C=0,N=st(),k=st(),E=st(),S=!1,A=function(e,t){return e===t?(S=!0,0):0},j=typeof t,D=1<<31,L={}.hasOwnProperty,H=[],q=H.pop,_=H.push,M=H.push,O=H.slice,F=H.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},B="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",P="[\\x20\\t\\r\\n\\f]",R="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",W=R.replace("w","w#"),$="\\["+P+"*("+R+")"+P+"*(?:([*^$|!~]?=)"+P+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+W+")|)|)"+P+"*\\]",I=":("+R+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+$.replace(3,8)+")*)|.*)\\)|)",z=RegExp("^"+P+"+|((?:^|[^\\\\])(?:\\\\.)*)"+P+"+$","g"),X=RegExp("^"+P+"*,"+P+"*"),U=RegExp("^"+P+"*([>+~]|"+P+")"+P+"*"),V=RegExp(P+"*[+~]"),Y=RegExp("="+P+"*([^\\]'\"]*)"+P+"*\\]","g"),J=RegExp(I),G=RegExp("^"+W+"$"),Q={ID:RegExp("^#("+R+")"),CLASS:RegExp("^\\.("+R+")"),TAG:RegExp("^("+R.replace("w","w*")+")"),ATTR:RegExp("^"+$),PSEUDO:RegExp("^"+I),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+P+"*(even|odd|(([+-]|)(\\d*)n|)"+P+"*(?:([+-]|)"+P+"*(\\d+)|))"+P+"*\\)|)","i"),bool:RegExp("^(?:"+B+")$","i"),needsContext:RegExp("^"+P+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+P+"*((?:-\\d)?\\d*)"+P+"*\\)|)(?=[^-]|$)","i")},K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,et=/^(?:input|select|textarea|button)$/i,tt=/^h\d$/i,nt=/'|\\/g,rt=RegExp("\\\\([\\da-f]{1,6}"+P+"?|("+P+")|.)","ig"),it=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(55296|r>>10,56320|1023&r)};try{M.apply(H=O.call(w.childNodes),w.childNodes),H[w.childNodes.length].nodeType}catch(ot){M={apply:H.length?function(e,t){_.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function at(e,t,n,i){var o,a,s,l,u,c,d,m,y,x;if((t?t.ownerDocument||t:w)!==f&&p(t),t=t||f,n=n||[],!e||"string"!=typeof e)return n;if(1!==(l=t.nodeType)&&9!==l)return[];if(h&&!i){if(o=Z.exec(e))if(s=o[1]){if(9===l){if(a=t.getElementById(s),!a||!a.parentNode)return n;if(a.id===s)return n.push(a),n}else if(t.ownerDocument&&(a=t.ownerDocument.getElementById(s))&&v(t,a)&&a.id===s)return n.push(a),n}else{if(o[2])return M.apply(n,t.getElementsByTagName(e)),n;if((s=o[3])&&r.getElementsByClassName&&t.getElementsByClassName)return M.apply(n,t.getElementsByClassName(s)),n}if(r.qsa&&(!g||!g.test(e))){if(m=d=b,y=t,x=9===l&&e,1===l&&"object"!==t.nodeName.toLowerCase()){c=mt(e),(d=t.getAttribute("id"))?m=d.replace(nt,"\\$&"):t.setAttribute("id",m),m="[id='"+m+"'] ",u=c.length;while(u--)c[u]=m+yt(c[u]);y=V.test(e)&&t.parentNode||t,x=c.join(",")}if(x)try{return M.apply(n,y.querySelectorAll(x)),n}catch(T){}finally{d||t.removeAttribute("id")}}}return kt(e.replace(z,"$1"),t,n,i)}function st(){var e=[];function t(n,r){return e.push(n+=" ")>o.cacheLength&&delete t[e.shift()],t[n]=r}return t}function lt(e){return e[b]=!0,e}function ut(e){var t=f.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function ct(e,t){var n=e.split("|"),r=e.length;while(r--)o.attrHandle[n[r]]=t}function pt(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function ft(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function dt(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function ht(e){return lt(function(t){return t=+t,lt(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}s=at.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},r=at.support={},p=at.setDocument=function(e){var n=e?e.ownerDocument||e:w,i=n.defaultView;return n!==f&&9===n.nodeType&&n.documentElement?(f=n,d=n.documentElement,h=!s(n),i&&i.attachEvent&&i!==i.top&&i.attachEvent("onbeforeunload",function(){p()}),r.attributes=ut(function(e){return e.className="i",!e.getAttribute("className")}),r.getElementsByTagName=ut(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),r.getElementsByClassName=ut(function(e){return e.innerHTML="<div class='a'></div><div class='a i'></div>",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),r.getById=ut(function(e){return d.appendChild(e).id=b,!n.getElementsByName||!n.getElementsByName(b).length}),r.getById?(o.find.ID=function(e,t){if(typeof t.getElementById!==j&&h){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){return e.getAttribute("id")===t}}):(delete o.find.ID,o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){var n=typeof e.getAttributeNode!==j&&e.getAttributeNode("id");return n&&n.value===t}}),o.find.TAG=r.getElementsByTagName?function(e,n){return typeof n.getElementsByTagName!==j?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},o.find.CLASS=r.getElementsByClassName&&function(e,n){return typeof n.getElementsByClassName!==j&&h?n.getElementsByClassName(e):t},m=[],g=[],(r.qsa=K.test(n.querySelectorAll))&&(ut(function(e){e.innerHTML="<select><option selected=''></option></select>",e.querySelectorAll("[selected]").length||g.push("\\["+P+"*(?:value|"+B+")"),e.querySelectorAll(":checked").length||g.push(":checked")}),ut(function(e){var t=n.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&g.push("[*^$]="+P+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||g.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),g.push(",.*:")})),(r.matchesSelector=K.test(y=d.webkitMatchesSelector||d.mozMatchesSelector||d.oMatchesSelector||d.msMatchesSelector))&&ut(function(e){r.disconnectedMatch=y.call(e,"div"),y.call(e,"[s!='']:x"),m.push("!=",I)}),g=g.length&&RegExp(g.join("|")),m=m.length&&RegExp(m.join("|")),v=K.test(d.contains)||d.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},A=d.compareDocumentPosition?function(e,t){if(e===t)return S=!0,0;var i=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t);return i?1&i||!r.sortDetached&&t.compareDocumentPosition(e)===i?e===n||v(w,e)?-1:t===n||v(w,t)?1:c?F.call(c,e)-F.call(c,t):0:4&i?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return S=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:c?F.call(c,e)-F.call(c,t):0;if(o===a)return pt(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?pt(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},n):f},at.matches=function(e,t){return at(e,null,null,t)},at.matchesSelector=function(e,t){if((e.ownerDocument||e)!==f&&p(e),t=t.replace(Y,"='$1']"),!(!r.matchesSelector||!h||m&&m.test(t)||g&&g.test(t)))try{var n=y.call(e,t);if(n||r.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(i){}return at(t,f,null,[e]).length>0},at.contains=function(e,t){return(e.ownerDocument||e)!==f&&p(e),v(e,t)},at.attr=function(e,n){(e.ownerDocument||e)!==f&&p(e);var i=o.attrHandle[n.toLowerCase()],a=i&&L.call(o.attrHandle,n.toLowerCase())?i(e,n,!h):t;return a===t?r.attributes||!h?e.getAttribute(n):(a=e.getAttributeNode(n))&&a.specified?a.value:null:a},at.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},at.uniqueSort=function(e){var t,n=[],i=0,o=0;if(S=!r.detectDuplicates,c=!r.sortStable&&e.slice(0),e.sort(A),S){while(t=e[o++])t===e[o]&&(i=n.push(o));while(i--)e.splice(n[i],1)}return e},a=at.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=a(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=a(t);return n},o=at.selectors={cacheLength:50,createPseudo:lt,match:Q,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(rt,it),e[3]=(e[4]||e[5]||"").replace(rt,it),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||at.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&at.error(e[0]),e},PSEUDO:function(e){var n,r=!e[5]&&e[2];return Q.CHILD.test(e[0])?null:(e[3]&&e[4]!==t?e[2]=e[4]:r&&J.test(r)&&(n=mt(r,!0))&&(n=r.indexOf(")",r.length-n)-r.length)&&(e[0]=e[0].slice(0,n),e[2]=r.slice(0,n)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(rt,it).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=N[e+" "];return t||(t=RegExp("(^|"+P+")"+e+"("+P+"|$)"))&&N(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==j&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=at.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var u,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!l&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[b]||(m[b]={}),u=c[e]||[],d=u[0]===T&&u[1],f=u[0]===T&&u[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[T,d,f];break}}else if(v&&(u=(t[b]||(t[b]={}))[e])&&u[0]===T)f=u[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[b]||(p[b]={}))[e]=[T,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=o.pseudos[e]||o.setFilters[e.toLowerCase()]||at.error("unsupported pseudo: "+e);return r[b]?r(t):r.length>1?(n=[e,e,"",t],o.setFilters.hasOwnProperty(e.toLowerCase())?lt(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=F.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:lt(function(e){var t=[],n=[],r=l(e.replace(z,"$1"));return r[b]?lt(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:lt(function(e){return function(t){return at(e,t).length>0}}),contains:lt(function(e){return function(t){return(t.textContent||t.innerText||a(t)).indexOf(e)>-1}}),lang:lt(function(e){return G.test(e||"")||at.error("unsupported lang: "+e),e=e.replace(rt,it).toLowerCase(),function(t){var n;do if(n=h?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===d},focus:function(e){return e===f.activeElement&&(!f.hasFocus||f.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!o.pseudos.empty(e)},header:function(e){return tt.test(e.nodeName)},input:function(e){return et.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:ht(function(){return[0]}),last:ht(function(e,t){return[t-1]}),eq:ht(function(e,t,n){return[0>n?n+t:n]}),even:ht(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:ht(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:ht(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:ht(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}},o.pseudos.nth=o.pseudos.eq;for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})o.pseudos[n]=ft(n);for(n in{submit:!0,reset:!0})o.pseudos[n]=dt(n);function gt(){}gt.prototype=o.filters=o.pseudos,o.setFilters=new gt;function mt(e,t){var n,r,i,a,s,l,u,c=k[e+" "];if(c)return t?0:c.slice(0);s=e,l=[],u=o.preFilter;while(s){(!n||(r=X.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),l.push(i=[])),n=!1,(r=U.exec(s))&&(n=r.shift(),i.push({value:n,type:r[0].replace(z," ")}),s=s.slice(n.length));for(a in o.filter)!(r=Q[a].exec(s))||u[a]&&!(r=u[a](r))||(n=r.shift(),i.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?at.error(e):k(e,l).slice(0)}function yt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function vt(e,t,n){var r=t.dir,o=n&&"parentNode"===r,a=C++;return t.first?function(t,n,i){while(t=t[r])if(1===t.nodeType||o)return e(t,n,i)}:function(t,n,s){var l,u,c,p=T+" "+a;if(s){while(t=t[r])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[r])if(1===t.nodeType||o)if(c=t[b]||(t[b]={}),(u=c[r])&&u[0]===p){if((l=u[1])===!0||l===i)return l===!0}else if(u=c[r]=[p],u[1]=e(t,n,s)||i,u[1]===!0)return!0}}function bt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function xt(e,t,n,r,i){var o,a=[],s=0,l=e.length,u=null!=t;for(;l>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),u&&t.push(s));return a}function wt(e,t,n,r,i,o){return r&&!r[b]&&(r=wt(r)),i&&!i[b]&&(i=wt(i,o)),lt(function(o,a,s,l){var u,c,p,f=[],d=[],h=a.length,g=o||Nt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:xt(g,f,e,s,l),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,l),r){u=xt(y,d),r(u,[],s,l),c=u.length;while(c--)(p=u[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){u=[],c=y.length;while(c--)(p=y[c])&&u.push(m[c]=p);i(null,y=[],u,l)}c=y.length;while(c--)(p=y[c])&&(u=i?F.call(o,p):f[c])>-1&&(o[u]=!(a[u]=p))}}else y=xt(y===a?y.splice(h,y.length):y),i?i(null,a,y,l):M.apply(a,y)})}function Tt(e){var t,n,r,i=e.length,a=o.relative[e[0].type],s=a||o.relative[" "],l=a?1:0,c=vt(function(e){return e===t},s,!0),p=vt(function(e){return F.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==u)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;i>l;l++)if(n=o.relative[e[l].type])f=[vt(bt(f),n)];else{if(n=o.filter[e[l].type].apply(null,e[l].matches),n[b]){for(r=++l;i>r;r++)if(o.relative[e[r].type])break;return wt(l>1&&bt(f),l>1&&yt(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(z,"$1"),n,r>l&&Tt(e.slice(l,r)),i>r&&Tt(e=e.slice(r)),i>r&&yt(e))}f.push(n)}return bt(f)}function Ct(e,t){var n=0,r=t.length>0,a=e.length>0,s=function(s,l,c,p,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,C=u,N=s||a&&o.find.TAG("*",d&&l.parentNode||l),k=T+=null==C?1:Math.random()||.1;for(w&&(u=l!==f&&l,i=n);null!=(h=N[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,l,c)){p.push(h);break}w&&(T=k,i=++n)}r&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,r&&b!==v){g=0;while(m=t[g++])m(x,y,l,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=q.call(p));y=xt(y)}M.apply(p,y),w&&!s&&y.length>0&&v+t.length>1&&at.uniqueSort(p)}return w&&(T=k,u=C),x};return r?lt(s):s}l=at.compile=function(e,t){var n,r=[],i=[],o=E[e+" "];if(!o){t||(t=mt(e)),n=t.length;while(n--)o=Tt(t[n]),o[b]?r.push(o):i.push(o);o=E(e,Ct(i,r))}return o};function Nt(e,t,n){var r=0,i=t.length;for(;i>r;r++)at(e,t[r],n);return n}function kt(e,t,n,i){var a,s,u,c,p,f=mt(e);if(!i&&1===f.length){if(s=f[0]=f[0].slice(0),s.length>2&&"ID"===(u=s[0]).type&&r.getById&&9===t.nodeType&&h&&o.relative[s[1].type]){if(t=(o.find.ID(u.matches[0].replace(rt,it),t)||[])[0],!t)return n;e=e.slice(s.shift().value.length)}a=Q.needsContext.test(e)?0:s.length;while(a--){if(u=s[a],o.relative[c=u.type])break;if((p=o.find[c])&&(i=p(u.matches[0].replace(rt,it),V.test(s[0].type)&&t.parentNode||t))){if(s.splice(a,1),e=i.length&&yt(s),!e)return M.apply(n,i),n;break}}}return l(e,f)(i,t,!h,n,V.test(e)),n}r.sortStable=b.split("").sort(A).join("")===b,r.detectDuplicates=S,p(),r.sortDetached=ut(function(e){return 1&e.compareDocumentPosition(f.createElement("div"))}),ut(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||ct("type|href|height|width",function(e,n,r){return r?t:e.getAttribute(n,"type"===n.toLowerCase()?1:2)}),r.attributes&&ut(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||ct("value",function(e,n,r){return r||"input"!==e.nodeName.toLowerCase()?t:e.defaultValue}),ut(function(e){return null==e.getAttribute("disabled")})||ct(B,function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&i.specified?i.value:e[n]===!0?n.toLowerCase():null}),x.find=at,x.expr=at.selectors,x.expr[":"]=x.expr.pseudos,x.unique=at.uniqueSort,x.text=at.getText,x.isXMLDoc=at.isXML,x.contains=at.contains}(e);var O={};function F(e){var t=O[e]={};return x.each(e.match(T)||[],function(e,n){t[n]=!0}),t}x.Callbacks=function(e){e="string"==typeof e?O[e]||F(e):x.extend({},e);var n,r,i,o,a,s,l=[],u=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=l.length,n=!0;l&&o>a;a++)if(l[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,l&&(u?u.length&&c(u.shift()):r?l=[]:p.disable())},p={add:function(){if(l){var t=l.length;(function i(t){x.each(t,function(t,n){var r=x.type(n);"function"===r?e.unique&&p.has(n)||l.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=l.length:r&&(s=t,c(r))}return this},remove:function(){return l&&x.each(arguments,function(e,t){var r;while((r=x.inArray(t,l,r))>-1)l.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?x.inArray(e,l)>-1:!(!l||!l.length)},empty:function(){return l=[],o=0,this},disable:function(){return l=u=r=t,this},disabled:function(){return!l},lock:function(){return u=t,r||p.disable(),this},locked:function(){return!u},fireWith:function(e,t){return!l||i&&!u||(t=t||[],t=[e,t.slice?t.slice():t],n?u.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},x.extend({Deferred:function(e){var t=[["resolve","done",x.Callbacks("once memory"),"resolved"],["reject","fail",x.Callbacks("once memory"),"rejected"],["notify","progress",x.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return x.Deferred(function(n){x.each(t,function(t,o){var a=o[0],s=x.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&x.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?x.extend(e,r):r}},i={};return r.pipe=r.then,x.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=g.call(arguments),r=n.length,i=1!==r||e&&x.isFunction(e.promise)?r:0,o=1===i?e:x.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?g.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,l,u;if(r>1)for(s=Array(r),l=Array(r),u=Array(r);r>t;t++)n[t]&&x.isFunction(n[t].promise)?n[t].promise().done(a(t,u,n)).fail(o.reject).progress(a(t,l,s)):--i;return i||o.resolveWith(u,n),o.promise()}}),x.support=function(t){var n,r,o,s,l,u,c,p,f,d=a.createElement("div");if(d.setAttribute("className","t"),d.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",n=d.getElementsByTagName("*")||[],r=d.getElementsByTagName("a")[0],!r||!r.style||!n.length)return t;s=a.createElement("select"),u=s.appendChild(a.createElement("option")),o=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t.getSetAttribute="t"!==d.className,t.leadingWhitespace=3===d.firstChild.nodeType,t.tbody=!d.getElementsByTagName("tbody").length,t.htmlSerialize=!!d.getElementsByTagName("link").length,t.style=/top/.test(r.getAttribute("style")),t.hrefNormalized="/a"===r.getAttribute("href"),t.opacity=/^0.5/.test(r.style.opacity),t.cssFloat=!!r.style.cssFloat,t.checkOn=!!o.value,t.optSelected=u.selected,t.enctype=!!a.createElement("form").enctype,t.html5Clone="<:nav></:nav>"!==a.createElement("nav").cloneNode(!0).outerHTML,t.inlineBlockNeedsLayout=!1,t.shrinkWrapBlocks=!1,t.pixelPosition=!1,t.deleteExpando=!0,t.noCloneEvent=!0,t.reliableMarginRight=!0,t.boxSizingReliable=!0,o.checked=!0,t.noCloneChecked=o.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!u.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}o=a.createElement("input"),o.setAttribute("value",""),t.input=""===o.getAttribute("value"),o.value="t",o.setAttribute("type","radio"),t.radioValue="t"===o.value,o.setAttribute("checked","t"),o.setAttribute("name","t"),l=a.createDocumentFragment(),l.appendChild(o),t.appendChecked=o.checked,t.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip;for(f in x(t))break;return t.ownLast="0"!==f,x(function(){var n,r,o,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",l=a.getElementsByTagName("body")[0];l&&(n=a.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",l.appendChild(n).appendChild(d),d.innerHTML="<table><tr><td></td><td>t</td></tr></table>",o=d.getElementsByTagName("td"),o[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===o[0].offsetHeight,o[0].style.display="",o[1].style.display="none",t.reliableHiddenOffsets=p&&0===o[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",x.swap(l,null!=l.style.zoom?{zoom:1}:{},function(){t.boxSizing=4===d.offsetWidth}),e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(a.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="<div></div>",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(l.style.zoom=1)),l.removeChild(n),n=d=o=r=null)}),n=s=l=u=r=o=null,t +}({});var B=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;function R(e,n,r,i){if(x.acceptData(e)){var o,a,s=x.expando,l=e.nodeType,u=l?x.cache:e,c=l?e[s]:e[s]&&s;if(c&&u[c]&&(i||u[c].data)||r!==t||"string"!=typeof n)return c||(c=l?e[s]=p.pop()||x.guid++:s),u[c]||(u[c]=l?{}:{toJSON:x.noop}),("object"==typeof n||"function"==typeof n)&&(i?u[c]=x.extend(u[c],n):u[c].data=x.extend(u[c].data,n)),a=u[c],i||(a.data||(a.data={}),a=a.data),r!==t&&(a[x.camelCase(n)]=r),"string"==typeof n?(o=a[n],null==o&&(o=a[x.camelCase(n)])):o=a,o}}function W(e,t,n){if(x.acceptData(e)){var r,i,o=e.nodeType,a=o?x.cache:e,s=o?e[x.expando]:x.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){x.isArray(t)?t=t.concat(x.map(t,x.camelCase)):t in r?t=[t]:(t=x.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;while(i--)delete r[t[i]];if(n?!I(r):!x.isEmptyObject(r))return}(n||(delete a[s].data,I(a[s])))&&(o?x.cleanData([e],!0):x.support.deleteExpando||a!=a.window?delete a[s]:a[s]=null)}}}x.extend({cache:{},noData:{applet:!0,embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?x.cache[e[x.expando]]:e[x.expando],!!e&&!I(e)},data:function(e,t,n){return R(e,t,n)},removeData:function(e,t){return W(e,t)},_data:function(e,t,n){return R(e,t,n,!0)},_removeData:function(e,t){return W(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&x.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),x.fn.extend({data:function(e,n){var r,i,o=null,a=0,s=this[0];if(e===t){if(this.length&&(o=x.data(s),1===s.nodeType&&!x._data(s,"parsedAttrs"))){for(r=s.attributes;r.length>a;a++)i=r[a].name,0===i.indexOf("data-")&&(i=x.camelCase(i.slice(5)),$(s,i,o[i]));x._data(s,"parsedAttrs",!0)}return o}return"object"==typeof e?this.each(function(){x.data(this,e)}):arguments.length>1?this.each(function(){x.data(this,e,n)}):s?$(s,e,x.data(s,e)):null},removeData:function(e){return this.each(function(){x.removeData(this,e)})}});function $(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(P,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:B.test(r)?x.parseJSON(r):r}catch(o){}x.data(e,n,r)}else r=t}return r}function I(e){var t;for(t in e)if(("data"!==t||!x.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}x.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=x._data(e,n),r&&(!i||x.isArray(r)?i=x._data(e,n,x.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),a=function(){x.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return x._data(e,n)||x._data(e,n,{empty:x.Callbacks("once memory").add(function(){x._removeData(e,t+"queue"),x._removeData(e,n)})})}}),x.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?x.queue(this[0],e):n===t?this:this.each(function(){var t=x.queue(this,e,n);x._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=x.Deferred(),a=this,s=this.length,l=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=x._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(l));return l(),o.promise(n)}});var z,X,U=/[\t\r\n\f]/g,V=/\r/g,Y=/^(?:input|select|textarea|button|object)$/i,J=/^(?:a|area)$/i,G=/^(?:checked|selected)$/i,Q=x.support.getSetAttribute,K=x.support.input;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){return x.access(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return e=x.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,l="string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).addClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=x.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,l=0===arguments.length||"string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).removeClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?x.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):x.isFunction(e)?this.each(function(n){x(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var t,r=0,o=x(this),a=e.match(T)||[];while(t=a[r++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else(n===i||"boolean"===n)&&(this.className&&x._data(this,"__className__",this.className),this.className=this.className||e===!1?"":x._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(U," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=x.isFunction(e),this.each(function(n){var o;1===this.nodeType&&(o=i?e.call(this,n,x(this).val()):e,null==o?o="":"number"==typeof o?o+="":x.isArray(o)&&(o=x.map(o,function(e){return null==e?"":e+""})),r=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=x.valHooks[o.type]||x.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(V,""):null==n?"":n)}}}),x.extend({valHooks:{option:{get:function(e){var t=x.find.attr(e,"value");return null!=t?t:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,l=0>i?s:o?i:0;for(;s>l;l++)if(n=r[l],!(!n.selected&&l!==i||(x.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&x.nodeName(n.parentNode,"optgroup"))){if(t=x(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n,r,i=e.options,o=x.makeArray(t),a=i.length;while(a--)r=i[a],(r.selected=x.inArray(x(r).val(),o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}},attr:function(e,n,r){var o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===i?x.prop(e,n,r):(1===s&&x.isXMLDoc(e)||(n=n.toLowerCase(),o=x.attrHooks[n]||(x.expr.match.bool.test(n)?X:z)),r===t?o&&"get"in o&&null!==(a=o.get(e,n))?a:(a=x.find.attr(e,n),null==a?t:a):null!==r?o&&"set"in o&&(a=o.set(e,r,n))!==t?a:(e.setAttribute(n,r+""),r):(x.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(T);if(o&&1===e.nodeType)while(n=o[i++])r=x.propFix[n]||n,x.expr.match.bool.test(n)?K&&Q||!G.test(n)?e[r]=!1:e[x.camelCase("default-"+n)]=e[r]=!1:x.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!x.support.radioValue&&"radio"===t&&x.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{"for":"htmlFor","class":"className"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!x.isXMLDoc(e),a&&(n=x.propFix[n]||n,o=x.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var t=x.find.attr(e,"tabindex");return t?parseInt(t,10):Y.test(e.nodeName)||J.test(e.nodeName)&&e.href?0:-1}}}}),X={set:function(e,t,n){return t===!1?x.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&x.propFix[n]||n,n):e[x.camelCase("default-"+n)]=e[n]=!0,n}},x.each(x.expr.match.bool.source.match(/\w+/g),function(e,n){var r=x.expr.attrHandle[n]||x.find.attr;x.expr.attrHandle[n]=K&&Q||!G.test(n)?function(e,n,i){var o=x.expr.attrHandle[n],a=i?t:(x.expr.attrHandle[n]=t)!=r(e,n,i)?n.toLowerCase():null;return x.expr.attrHandle[n]=o,a}:function(e,n,r){return r?t:e[x.camelCase("default-"+n)]?n.toLowerCase():null}}),K&&Q||(x.attrHooks.value={set:function(e,n,r){return x.nodeName(e,"input")?(e.defaultValue=n,t):z&&z.set(e,n,r)}}),Q||(z={set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},x.expr.attrHandle.id=x.expr.attrHandle.name=x.expr.attrHandle.coords=function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&""!==i.value?i.value:null},x.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&r.specified?r.value:t},set:z.set},x.attrHooks.contenteditable={set:function(e,t,n){z.set(e,""===t?!1:t,n)}},x.each(["width","height"],function(e,n){x.attrHooks[n]={set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}}})),x.support.hrefNormalized||x.each(["href","src"],function(e,t){x.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),x.support.style||(x.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),x.support.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){x.propFix[this.toLowerCase()]=this}),x.support.enctype||(x.propFix.enctype="encoding"),x.each(["radio","checkbox"],function(){x.valHooks[this]={set:function(e,n){return x.isArray(n)?e.checked=x.inArray(x(e).val(),n)>=0:t}},x.support.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}function at(){try{return a.activeElement}catch(e){}}x.event={global:{},add:function(e,n,r,o,a){var s,l,u,c,p,f,d,h,g,m,y,v=x._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=x.guid++),(l=v.events)||(l=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof x===i||e&&x.event.triggered===e.type?t:x.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(T)||[""],u=n.length;while(u--)s=rt.exec(n[u])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),g&&(p=x.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=x.event.special[g]||{},d=x.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&x.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=l[g])||(h=l[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),x.event.global[g]=!0);e=null}},remove:function(e,t,n,r,i){var o,a,s,l,u,c,p,f,d,h,g,m=x.hasData(e)&&x._data(e);if(m&&(c=m.events)){t=(t||"").match(T)||[""],u=t.length;while(u--)if(s=rt.exec(t[u])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=x.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),l=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));l&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||x.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)x.event.remove(e,d+t[u],n,r,!0);x.isEmptyObject(c)&&(delete m.handle,x._removeData(e,"events"))}},trigger:function(n,r,i,o){var s,l,u,c,p,f,d,h=[i||a],g=v.call(n,"type")?n.type:n,m=v.call(n,"namespace")?n.namespace.split("."):[];if(u=f=i=i||a,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+x.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),l=0>g.indexOf(":")&&"on"+g,n=n[x.expando]?n:new x.Event(g,"object"==typeof n&&n),n.isTrigger=o?2:3,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:x.makeArray(r,[n]),p=x.event.special[g]||{},o||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!o&&!p.noBubble&&!x.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(u=u.parentNode);u;u=u.parentNode)h.push(u),f=u;f===(i.ownerDocument||a)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((u=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(x._data(u,"events")||{})[n.type]&&x._data(u,"handle"),s&&s.apply(u,r),s=l&&u[l],s&&x.acceptData(u)&&s.apply&&s.apply(u,r)===!1&&n.preventDefault();if(n.type=g,!o&&!n.isDefaultPrevented()&&(!p._default||p._default.apply(h.pop(),r)===!1)&&x.acceptData(i)&&l&&i[g]&&!x.isWindow(i)){f=i[l],f&&(i[l]=null),x.event.triggered=g;try{i[g]()}catch(y){}x.event.triggered=t,f&&(i[l]=f)}return n.result}},dispatch:function(e){e=x.event.fix(e);var n,r,i,o,a,s=[],l=g.call(arguments),u=(x._data(this,"events")||{})[e.type]||[],c=x.event.special[e.type]||{};if(l[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=x.event.handlers.call(this,e,u),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((x.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,l),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],l=n.delegateCount,u=e.target;if(l&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(o=[],a=0;l>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?x(r,this).index(u)>=0:x.find(r,this,null,[u]).length),o[r]&&o.push(i);o.length&&s.push({elem:u,handlers:o})}return n.length>l&&s.push({elem:this,handlers:n.slice(l)}),s},fix:function(e){if(e[x.expando])return e;var t,n,r,i=e.type,o=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new x.Event(o),t=r.length;while(t--)n=r[t],e[n]=o[n];return e.target||(e.target=o.srcElement||a),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,o):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,o,s=n.button,l=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||a,o=i.documentElement,r=i.body,e.pageX=n.clientX+(o&&o.scrollLeft||r&&r.scrollLeft||0)-(o&&o.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(o&&o.scrollTop||r&&r.scrollTop||0)-(o&&o.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&l&&(e.relatedTarget=l===e.target?n.toElement:l),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==at()&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===at()&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},click:{trigger:function(){return x.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t},_default:function(e){return x.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=x.extend(new x.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?x.event.trigger(i,null,t):x.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},x.removeEvent=a.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},x.Event=function(e,n){return this instanceof x.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&x.extend(this,n),this.timeStamp=e&&e.timeStamp||x.now(),this[x.expando]=!0,t):new x.Event(e,n)},x.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},x.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){x.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!x.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),x.support.submitBubbles||(x.event.special.submit={setup:function(){return x.nodeName(this,"form")?!1:(x.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=x.nodeName(n,"input")||x.nodeName(n,"button")?n.form:t;r&&!x._data(r,"submitBubbles")&&(x.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),x._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&x.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return x.nodeName(this,"form")?!1:(x.event.remove(this,"._submit"),t)}}),x.support.changeBubbles||(x.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(x.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),x.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),x.event.simulate("change",this,e,!0)})),!1):(x.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!x._data(t,"changeBubbles")&&(x.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||x.event.simulate("change",this.parentNode,e,!0)}),x._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return x.event.remove(this,"._change"),!Z.test(this.nodeName)}}),x.support.focusinBubbles||x.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){x.event.simulate(t,e.target,x.event.fix(e),!0)};x.event.special[t]={setup:function(){0===n++&&a.addEventListener(e,r,!0)},teardown:function(){0===--n&&a.removeEventListener(e,r,!0)}}}),x.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return x().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=x.guid++)),this.each(function(){x.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,x(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){x.event.remove(this,e,r,n)})},trigger:function(e,t){return this.each(function(){x.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?x.event.trigger(e,n,r,!0):t}});var st=/^.[^:#\[\.,]*$/,lt=/^(?:parents|prev(?:Until|All))/,ut=x.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};x.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(x(e).filter(function(){for(t=0;i>t;t++)if(x.contains(r[t],this))return!0}));for(t=0;i>t;t++)x.find(e,r[t],n);return n=this.pushStack(i>1?x.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},has:function(e){var t,n=x(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(x.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e||[],!0))},filter:function(e){return this.pushStack(ft(this,e||[],!1))},is:function(e){return!!ft(this,"string"==typeof e&&ut.test(e)?x(e):e||[],!1).length},closest:function(e,t){var n,r=0,i=this.length,o=[],a=ut.test(e)||"string"!=typeof e?x(e,t||this.context):0;for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(11>n.nodeType&&(a?a.index(n)>-1:1===n.nodeType&&x.find.matchesSelector(n,e))){n=o.push(n);break}return this.pushStack(o.length>1?x.unique(o):o)},index:function(e){return e?"string"==typeof e?x.inArray(this[0],x(e)):x.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?x(e,t):x.makeArray(e&&e.nodeType?[e]:e),r=x.merge(this.get(),n);return this.pushStack(x.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return x.dir(e,"parentNode")},parentsUntil:function(e,t,n){return x.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return x.dir(e,"nextSibling")},prevAll:function(e){return x.dir(e,"previousSibling")},nextUntil:function(e,t,n){return x.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return x.dir(e,"previousSibling",n)},siblings:function(e){return x.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return x.sibling(e.firstChild)},contents:function(e){return x.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:x.merge([],e.childNodes)}},function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(ct[e]||(i=x.unique(i)),lt.test(e)&&(i=i.reverse())),this.pushStack(i)}}),x.extend({filter:function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,function(e){return 1===e.nodeType}))},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!x(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(st.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return x.inArray(e,t)>=0!==n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/<tbody/i,wt=/<|&#?\w+;/,Tt=/<(?:script|style|link)/i,Ct=/^(?:checkbox|radio)$/i,Nt=/checked\s*(?:[^=]|=\s*.checked.)/i,kt=/^$|\/(?:java|ecma)script/i,Et=/^true\/(.*)/,St=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,At={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],area:[1,"<map>","</map>"],param:[1,"<object>","</object>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:x.support.htmlSerialize?[0,"",""]:[1,"X<div>","</div>"]},jt=dt(a),Dt=jt.appendChild(a.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,x.fn.extend({text:function(e){return x.access(this,function(e){return e===t?x.text(this):this.empty().append((this[0]&&this[0].ownerDocument||a).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=e?x.filter(e,this):this,i=0;for(;null!=(n=r[i]);i++)t||1!==n.nodeType||x.cleanData(Ft(n)),n.parentNode&&(t&&x.contains(n.ownerDocument,n)&&_t(Ft(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&x.cleanData(Ft(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&x.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return x.clone(this,e,t)})},html:function(e){return x.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!x.support.htmlSerialize&&mt.test(e)||!x.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1></$2>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(x.cleanData(Ft(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=x.map(this,function(e){return[e.nextSibling,e.parentNode]}),t=0;return this.domManip(arguments,function(n){var r=e[t++],i=e[t++];i&&(r&&r.parentNode!==i&&(r=this.nextSibling),x(this).remove(),i.insertBefore(n,r))},!0),t?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t,n){e=d.apply([],e);var r,i,o,a,s,l,u=0,c=this.length,p=this,f=c-1,h=e[0],g=x.isFunction(h);if(g||!(1>=c||"string"!=typeof h||x.support.checkClone)&&Nt.test(h))return this.each(function(r){var i=p.eq(r);g&&(e[0]=h.call(this,r,i.html())),i.domManip(e,t,n)});if(c&&(l=x.buildFragment(e,this[0].ownerDocument,!1,!n&&this),r=l.firstChild,1===l.childNodes.length&&(l=r),r)){for(a=x.map(Ft(l,"script"),Ht),o=a.length;c>u;u++)i=l,u!==f&&(i=x.clone(i,!0,!0),o&&x.merge(a,Ft(i,"script"))),t.call(this[u],i,u);if(o)for(s=a[a.length-1].ownerDocument,x.map(a,qt),u=0;o>u;u++)i=a[u],kt.test(i.type||"")&&!x._data(i,"globalEval")&&x.contains(s,i)&&(i.src?x._evalUrl(i.src):x.globalEval((i.text||i.textContent||i.innerHTML||"").replace(St,"")));l=r=null}return this}});function Lt(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function Ht(e){return e.type=(null!==x.find.attr(e,"type"))+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function _t(e,t){var n,r=0;for(;null!=(n=e[r]);r++)x._data(n,"globalEval",!t||x._data(t[r],"globalEval"))}function Mt(e,t){if(1===t.nodeType&&x.hasData(e)){var n,r,i,o=x._data(e),a=x._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)x.event.add(t,n,s[n][r])}a.data&&(a.data=x.extend({},a.data))}}function Ot(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!x.support.noCloneEvent&&t[x.expando]){i=x._data(t);for(r in i.events)x.removeEvent(t,r,i.handle);t.removeAttribute(x.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),x.support.html5Clone&&e.innerHTML&&!x.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Ct.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}x.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){x.fn[e]=function(e){var n,r=0,i=[],o=x(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),x(o[r])[t](n),h.apply(i,n.get());return this.pushStack(i)}});function Ft(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||x.nodeName(o,n)?s.push(o):x.merge(s,Ft(o,n));return n===t||n&&x.nodeName(e,n)?x.merge([e],s):s}function Bt(e){Ct.test(e.type)&&(e.defaultChecked=e.checked)}x.extend({clone:function(e,t,n){var r,i,o,a,s,l=x.contains(e.ownerDocument,e);if(x.support.html5Clone||x.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(x.support.noCloneEvent&&x.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||x.isXMLDoc(e)))for(r=Ft(o),s=Ft(e),a=0;null!=(i=s[a]);++a)r[a]&&Ot(i,r[a]);if(t)if(n)for(s=s||Ft(e),r=r||Ft(o),a=0;null!=(i=s[a]);a++)Mt(i,r[a]);else Mt(e,o);return r=Ft(o,"script"),r.length>0&&_t(r,!l&&Ft(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,l,u,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===x.type(o))x.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),l=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[l]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1></$2>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!x.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!x.support.tbody){o="table"!==l||xt.test(o)?"<table>"!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)x.nodeName(u=o.childNodes[i],"tbody")&&!u.childNodes.length&&o.removeChild(u)}x.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),x.support.appendChecked||x.grep(Ft(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===x.inArray(o,r))&&(a=x.contains(o.ownerDocument,o),s=Ft(f.appendChild(o),"script"),a&&_t(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,l=x.expando,u=x.cache,c=x.support.deleteExpando,f=x.event.special;for(;null!=(n=e[s]);s++)if((t||x.acceptData(n))&&(o=n[l],a=o&&u[o])){if(a.events)for(r in a.events)f[r]?x.event.remove(n,r):x.removeEvent(n,r,a.handle); +u[o]&&(delete u[o],c?delete n[l]:typeof n.removeAttribute!==i?n.removeAttribute(l):n[l]=null,p.push(o))}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})}}),x.fn.extend({wrapAll:function(e){if(x.isFunction(e))return this.each(function(t){x(this).wrapAll(e.call(this,t))});if(this[0]){var t=x(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+w+")(.*)$","i"),Yt=RegExp("^("+w+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+w+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=x._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=x._data(r,"olddisplay",ln(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&x._data(r,"olddisplay",i?n:x.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}x.fn.extend({css:function(e,n){return x.access(this,function(e,n,r){var i,o,a={},s=0;if(x.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=x.css(e,n[s],!1,o);return a}return r!==t?x.style(e,n,r):x.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){nn(this)?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":x.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,l=x.camelCase(n),u=e.style;if(n=x.cssProps[l]||(x.cssProps[l]=tn(u,l)),s=x.cssHooks[n]||x.cssHooks[l],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:u[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(x.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||x.cssNumber[l]||(r+="px"),x.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(u[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{u[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,l=x.camelCase(n);return n=x.cssProps[l]||(x.cssProps[l]=tn(e.style,l)),s=x.cssHooks[n]||x.cssHooks[l],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||x.isNumeric(o)?o||0:a):a}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s.getPropertyValue(n)||s[n]:t,u=e.style;return s&&(""!==l||x.contains(e.ownerDocument,e)||(l=x.style(e,n)),Yt.test(l)&&Ut.test(n)&&(i=u.width,o=u.minWidth,a=u.maxWidth,u.minWidth=u.maxWidth=u.width=l,l=s.width,u.width=i,u.minWidth=o,u.maxWidth=a)),l}):a.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s[n]:t,u=e.style;return null==l&&u&&u[n]&&(l=u[n]),Yt.test(l)&&!zt.test(n)&&(i=u.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),u.left="fontSize"===n?"1em":l,l=u.pixelLeft+"px",u.left=i,a&&(o.left=a)),""===l?"auto":l});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=x.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=x.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=x.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=x.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=x.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function ln(e){var t=a,n=Gt[e];return n||(n=un(e,t),"none"!==n&&n||(Pt=(Pt||x("<iframe frameborder='0' width='0' height='0'/>").css("cssText","display:block !important")).appendTo(t.documentElement),t=(Pt[0].contentWindow||Pt[0].contentDocument).document,t.write("<!doctype html><html><body>"),t.close(),n=un(e,t),Pt.detach()),Gt[e]=n),n}function un(e,t){var n=x(t.createElement(e)).appendTo(t.body),r=x.css(n[0],"display");return n.remove(),r}x.each(["height","width"],function(e,n){x.cssHooks[n]={get:function(e,r,i){return r?0===e.offsetWidth&&Xt.test(x.css(e,"display"))?x.swap(e,Qt,function(){return sn(e,n,i)}):sn(e,n,i):t},set:function(e,t,r){var i=r&&Rt(e);return on(e,t,r?an(e,n,r,x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,i),i):0)}}}),x.support.opacity||(x.cssHooks.opacity={get:function(e,t){return It.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=x.isNumeric(t)?"alpha(opacity="+100*t+")":"",o=r&&r.filter||n.filter||"";n.zoom=1,(t>=1||""===t)&&""===x.trim(o.replace($t,""))&&n.removeAttribute&&(n.removeAttribute("filter"),""===t||r&&!r.filter)||(n.filter=$t.test(o)?o.replace($t,i):o+" "+i)}}),x(function(){x.support.reliableMarginRight||(x.cssHooks.marginRight={get:function(e,n){return n?x.swap(e,{display:"inline-block"},Wt,[e,"marginRight"]):t}}),!x.support.pixelPosition&&x.fn.position&&x.each(["top","left"],function(e,n){x.cssHooks[n]={get:function(e,r){return r?(r=Wt(e,n),Yt.test(r)?x(e).position()[n]+"px":r):t}}})}),x.expr&&x.expr.filters&&(x.expr.filters.hidden=function(e){return 0>=e.offsetWidth&&0>=e.offsetHeight||!x.support.reliableHiddenOffsets&&"none"===(e.style&&e.style.display||x.css(e,"display"))},x.expr.filters.visible=function(e){return!x.expr.filters.hidden(e)}),x.each({margin:"",padding:"",border:"Width"},function(e,t){x.cssHooks[e+t]={expand:function(n){var r=0,i={},o="string"==typeof n?n.split(" "):[n];for(;4>r;r++)i[e+Zt[r]+t]=o[r]||o[r-2]||o[0];return i}},Ut.test(e)||(x.cssHooks[e+t].set=on)});var cn=/%20/g,pn=/\[\]$/,fn=/\r?\n/g,dn=/^(?:submit|button|image|reset|file)$/i,hn=/^(?:input|select|textarea|keygen)/i;x.fn.extend({serialize:function(){return x.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=x.prop(this,"elements");return e?x.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!x(this).is(":disabled")&&hn.test(this.nodeName)&&!dn.test(e)&&(this.checked||!Ct.test(e))}).map(function(e,t){var n=x(this).val();return null==n?null:x.isArray(n)?x.map(n,function(e){return{name:t.name,value:e.replace(fn,"\r\n")}}):{name:t.name,value:n.replace(fn,"\r\n")}}).get()}}),x.param=function(e,n){var r,i=[],o=function(e,t){t=x.isFunction(t)?t():null==t?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(n===t&&(n=x.ajaxSettings&&x.ajaxSettings.traditional),x.isArray(e)||e.jquery&&!x.isPlainObject(e))x.each(e,function(){o(this.name,this.value)});else for(r in e)gn(r,e[r],n,o);return i.join("&").replace(cn,"+")};function gn(e,t,n,r){var i;if(x.isArray(t))x.each(t,function(t,i){n||pn.test(e)?r(e,i):gn(e+"["+("object"==typeof i?t:"")+"]",i,n,r)});else if(n||"object"!==x.type(t))r(e,t);else for(i in t)gn(e+"["+i+"]",t[i],n,r)}x.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 contextmenu".split(" "),function(e,t){x.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),x.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}});var mn,yn,vn=x.now(),bn=/\?/,xn=/#.*$/,wn=/([?&])_=[^&]*/,Tn=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Cn=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Nn=/^(?:GET|HEAD)$/,kn=/^\/\//,En=/^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,Sn=x.fn.load,An={},jn={},Dn="*/".concat("*");try{yn=o.href}catch(Ln){yn=a.createElement("a"),yn.href="",yn=yn.href}mn=En.exec(yn.toLowerCase())||[];function Hn(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(T)||[];if(x.isFunction(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function qn(e,n,r,i){var o={},a=e===jn;function s(l){var u;return o[l]=!0,x.each(e[l]||[],function(e,l){var c=l(n,r,i);return"string"!=typeof c||a||o[c]?a?!(u=c):t:(n.dataTypes.unshift(c),s(c),!1)}),u}return s(n.dataTypes[0])||!o["*"]&&s("*")}function _n(e,n){var r,i,o=x.ajaxSettings.flatOptions||{};for(i in n)n[i]!==t&&((o[i]?e:r||(r={}))[i]=n[i]);return r&&x.extend(!0,e,r),e}x.fn.load=function(e,n,r){if("string"!=typeof e&&Sn)return Sn.apply(this,arguments);var i,o,a,s=this,l=e.indexOf(" ");return l>=0&&(i=e.slice(l,e.length),e=e.slice(0,l)),x.isFunction(n)?(r=n,n=t):n&&"object"==typeof n&&(a="POST"),s.length>0&&x.ajax({url:e,type:a,dataType:"html",data:n}).done(function(e){o=arguments,s.html(i?x("<div>").append(x.parseHTML(e)).find(i):e)}).complete(r&&function(e,t){s.each(r,o||[e.responseText,t,e])}),this},x.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){x.fn[t]=function(e){return this.on(t,e)}}),x.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:yn,type:"GET",isLocal:Cn.test(mn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Dn,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":x.parseJSON,"text xml":x.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?_n(_n(e,x.ajaxSettings),t):_n(x.ajaxSettings,e)},ajaxPrefilter:Hn(An),ajaxTransport:Hn(jn),ajax:function(e,n){"object"==typeof e&&(n=e,e=t),n=n||{};var r,i,o,a,s,l,u,c,p=x.ajaxSetup({},n),f=p.context||p,d=p.context&&(f.nodeType||f.jquery)?x(f):x.event,h=x.Deferred(),g=x.Callbacks("once memory"),m=p.statusCode||{},y={},v={},b=0,w="canceled",C={readyState:0,getResponseHeader:function(e){var t;if(2===b){if(!c){c={};while(t=Tn.exec(a))c[t[1].toLowerCase()]=t[2]}t=c[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===b?a:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return b||(e=v[n]=v[n]||e,y[e]=t),this},overrideMimeType:function(e){return b||(p.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>b)for(t in e)m[t]=[m[t],e[t]];else C.always(e[C.status]);return this},abort:function(e){var t=e||w;return u&&u.abort(t),k(0,t),this}};if(h.promise(C).complete=g.add,C.success=C.done,C.error=C.fail,p.url=((e||p.url||yn)+"").replace(xn,"").replace(kn,mn[1]+"//"),p.type=n.method||n.type||p.method||p.type,p.dataTypes=x.trim(p.dataType||"*").toLowerCase().match(T)||[""],null==p.crossDomain&&(r=En.exec(p.url.toLowerCase()),p.crossDomain=!(!r||r[1]===mn[1]&&r[2]===mn[2]&&(r[3]||("http:"===r[1]?"80":"443"))===(mn[3]||("http:"===mn[1]?"80":"443")))),p.data&&p.processData&&"string"!=typeof p.data&&(p.data=x.param(p.data,p.traditional)),qn(An,p,n,C),2===b)return C;l=p.global,l&&0===x.active++&&x.event.trigger("ajaxStart"),p.type=p.type.toUpperCase(),p.hasContent=!Nn.test(p.type),o=p.url,p.hasContent||(p.data&&(o=p.url+=(bn.test(o)?"&":"?")+p.data,delete p.data),p.cache===!1&&(p.url=wn.test(o)?o.replace(wn,"$1_="+vn++):o+(bn.test(o)?"&":"?")+"_="+vn++)),p.ifModified&&(x.lastModified[o]&&C.setRequestHeader("If-Modified-Since",x.lastModified[o]),x.etag[o]&&C.setRequestHeader("If-None-Match",x.etag[o])),(p.data&&p.hasContent&&p.contentType!==!1||n.contentType)&&C.setRequestHeader("Content-Type",p.contentType),C.setRequestHeader("Accept",p.dataTypes[0]&&p.accepts[p.dataTypes[0]]?p.accepts[p.dataTypes[0]]+("*"!==p.dataTypes[0]?", "+Dn+"; q=0.01":""):p.accepts["*"]);for(i in p.headers)C.setRequestHeader(i,p.headers[i]);if(p.beforeSend&&(p.beforeSend.call(f,C,p)===!1||2===b))return C.abort();w="abort";for(i in{success:1,error:1,complete:1})C[i](p[i]);if(u=qn(jn,p,n,C)){C.readyState=1,l&&d.trigger("ajaxSend",[C,p]),p.async&&p.timeout>0&&(s=setTimeout(function(){C.abort("timeout")},p.timeout));try{b=1,u.send(y,k)}catch(N){if(!(2>b))throw N;k(-1,N)}}else k(-1,"No Transport");function k(e,n,r,i){var c,y,v,w,T,N=n;2!==b&&(b=2,s&&clearTimeout(s),u=t,a=i||"",C.readyState=e>0?4:0,c=e>=200&&300>e||304===e,r&&(w=Mn(p,C,r)),w=On(p,w,C,c),c?(p.ifModified&&(T=C.getResponseHeader("Last-Modified"),T&&(x.lastModified[o]=T),T=C.getResponseHeader("etag"),T&&(x.etag[o]=T)),204===e||"HEAD"===p.type?N="nocontent":304===e?N="notmodified":(N=w.state,y=w.data,v=w.error,c=!v)):(v=N,(e||!N)&&(N="error",0>e&&(e=0))),C.status=e,C.statusText=(n||N)+"",c?h.resolveWith(f,[y,N,C]):h.rejectWith(f,[C,N,v]),C.statusCode(m),m=t,l&&d.trigger(c?"ajaxSuccess":"ajaxError",[C,p,c?y:v]),g.fireWith(f,[C,N]),l&&(d.trigger("ajaxComplete",[C,p]),--x.active||x.event.trigger("ajaxStop")))}return C},getJSON:function(e,t,n){return x.get(e,t,n,"json")},getScript:function(e,n){return x.get(e,t,n,"script")}}),x.each(["get","post"],function(e,n){x[n]=function(e,r,i,o){return x.isFunction(r)&&(o=o||i,i=r,r=t),x.ajax({url:e,type:n,dataType:o,data:r,success:i})}});function Mn(e,n,r){var i,o,a,s,l=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),o===t&&(o=e.mimeType||n.getResponseHeader("Content-Type"));if(o)for(s in l)if(l[s]&&l[s].test(o)){u.unshift(s);break}if(u[0]in r)a=u[0];else{for(s in r){if(!u[0]||e.converters[s+" "+u[0]]){a=s;break}i||(i=s)}a=a||i}return a?(a!==u[0]&&u.unshift(a),r[a]):t}function On(e,t,n,r){var i,o,a,s,l,u={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)u[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!l&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),l=o,o=c.shift())if("*"===o)o=l;else if("*"!==l&&l!==o){if(a=u[l+" "+o]||u["* "+o],!a)for(i in u)if(s=i.split(" "),s[1]===o&&(a=u[l+" "+s[0]]||u["* "+s[0]])){a===!0?a=u[i]:u[i]!==!0&&(o=s[0],c.unshift(s[1]));break}if(a!==!0)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(p){return{state:"parsererror",error:a?p:"No conversion from "+l+" to "+o}}}return{state:"success",data:t}}x.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(e){return x.globalEval(e),e}}}),x.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),x.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=a.head||x("head")[0]||a.documentElement;return{send:function(t,i){n=a.createElement("script"),n.async=!0,e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,t){(t||!n.readyState||/loaded|complete/.test(n.readyState))&&(n.onload=n.onreadystatechange=null,n.parentNode&&n.parentNode.removeChild(n),n=null,t||i(200,"success"))},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(t,!0)}}}});var Fn=[],Bn=/(=)\?(?=&|$)|\?\?/;x.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Fn.pop()||x.expando+"_"+vn++;return this[e]=!0,e}}),x.ajaxPrefilter("json jsonp",function(n,r,i){var o,a,s,l=n.jsonp!==!1&&(Bn.test(n.url)?"url":"string"==typeof n.data&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Bn.test(n.data)&&"data");return l||"jsonp"===n.dataTypes[0]?(o=n.jsonpCallback=x.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,l?n[l]=n[l].replace(Bn,"$1"+o):n.jsonp!==!1&&(n.url+=(bn.test(n.url)?"&":"?")+n.jsonp+"="+o),n.converters["script json"]=function(){return s||x.error(o+" was not called"),s[0]},n.dataTypes[0]="json",a=e[o],e[o]=function(){s=arguments},i.always(function(){e[o]=a,n[o]&&(n.jsonpCallback=r.jsonpCallback,Fn.push(o)),s&&x.isFunction(a)&&a(s[0]),s=a=t}),"script"):t});var Pn,Rn,Wn=0,$n=e.ActiveXObject&&function(){var e;for(e in Pn)Pn[e](t,!0)};function In(){try{return new e.XMLHttpRequest}catch(t){}}function zn(){try{return new e.ActiveXObject("Microsoft.XMLHTTP")}catch(t){}}x.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&In()||zn()}:In,Rn=x.ajaxSettings.xhr(),x.support.cors=!!Rn&&"withCredentials"in Rn,Rn=x.support.ajax=!!Rn,Rn&&x.ajaxTransport(function(n){if(!n.crossDomain||x.support.cors){var r;return{send:function(i,o){var a,s,l=n.xhr();if(n.username?l.open(n.type,n.url,n.async,n.username,n.password):l.open(n.type,n.url,n.async),n.xhrFields)for(s in n.xhrFields)l[s]=n.xhrFields[s];n.mimeType&&l.overrideMimeType&&l.overrideMimeType(n.mimeType),n.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");try{for(s in i)l.setRequestHeader(s,i[s])}catch(u){}l.send(n.hasContent&&n.data||null),r=function(e,i){var s,u,c,p;try{if(r&&(i||4===l.readyState))if(r=t,a&&(l.onreadystatechange=x.noop,$n&&delete Pn[a]),i)4!==l.readyState&&l.abort();else{p={},s=l.status,u=l.getAllResponseHeaders(),"string"==typeof l.responseText&&(p.text=l.responseText);try{c=l.statusText}catch(f){c=""}s||!n.isLocal||n.crossDomain?1223===s&&(s=204):s=p.text?200:404}}catch(d){i||o(-1,d)}p&&o(s,c,p,u)},n.async?4===l.readyState?setTimeout(r):(a=++Wn,$n&&(Pn||(Pn={},x(e).unload($n)),Pn[a]=r),l.onreadystatechange=r):r()},abort:function(){r&&r(t,!0)}}}});var Xn,Un,Vn=/^(?:toggle|show|hide)$/,Yn=RegExp("^(?:([+-])=|)("+w+")([a-z%]*)$","i"),Jn=/queueHooks$/,Gn=[nr],Qn={"*":[function(e,t){var n=this.createTween(e,t),r=n.cur(),i=Yn.exec(t),o=i&&i[3]||(x.cssNumber[e]?"":"px"),a=(x.cssNumber[e]||"px"!==o&&+r)&&Yn.exec(x.css(n.elem,e)),s=1,l=20;if(a&&a[3]!==o){o=o||a[3],i=i||[],a=+r||1;do s=s||".5",a/=s,x.style(n.elem,e,a+o);while(s!==(s=n.cur()/r)&&1!==s&&--l)}return i&&(a=n.start=+a||+r||0,n.unit=o,n.end=i[1]?a+(i[1]+1)*i[2]:+i[2]),n}]};function Kn(){return setTimeout(function(){Xn=t}),Xn=x.now()}function Zn(e,t,n){var r,i=(Qn[t]||[]).concat(Qn["*"]),o=0,a=i.length;for(;a>o;o++)if(r=i[o].call(n,t,e))return r}function er(e,t,n){var r,i,o=0,a=Gn.length,s=x.Deferred().always(function(){delete l.elem}),l=function(){if(i)return!1;var t=Xn||Kn(),n=Math.max(0,u.startTime+u.duration-t),r=n/u.duration||0,o=1-r,a=0,l=u.tweens.length;for(;l>a;a++)u.tweens[a].run(o);return s.notifyWith(e,[u,o,n]),1>o&&l?n:(s.resolveWith(e,[u]),!1)},u=s.promise({elem:e,props:x.extend({},t),opts:x.extend(!0,{specialEasing:{}},n),originalProperties:t,originalOptions:n,startTime:Xn||Kn(),duration:n.duration,tweens:[],createTween:function(t,n){var r=x.Tween(e,u.opts,t,n,u.opts.specialEasing[t]||u.opts.easing);return u.tweens.push(r),r},stop:function(t){var n=0,r=t?u.tweens.length:0;if(i)return this;for(i=!0;r>n;n++)u.tweens[n].run(1);return t?s.resolveWith(e,[u,t]):s.rejectWith(e,[u,t]),this}}),c=u.props;for(tr(c,u.opts.specialEasing);a>o;o++)if(r=Gn[o].call(u,e,c,u.opts))return r;return x.map(c,Zn,u),x.isFunction(u.opts.start)&&u.opts.start.call(e,u),x.fx.timer(x.extend(l,{elem:e,anim:u,queue:u.opts.queue})),u.progress(u.opts.progress).done(u.opts.done,u.opts.complete).fail(u.opts.fail).always(u.opts.always)}function tr(e,t){var n,r,i,o,a;for(n in e)if(r=x.camelCase(n),i=t[r],o=e[n],x.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),a=x.cssHooks[r],a&&"expand"in a){o=a.expand(o),delete e[r];for(n in o)n in e||(e[n]=o[n],t[n]=i)}else t[r]=i}x.Animation=x.extend(er,{tweener:function(e,t){x.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");var n,r=0,i=e.length;for(;i>r;r++)n=e[r],Qn[n]=Qn[n]||[],Qn[n].unshift(t)},prefilter:function(e,t){t?Gn.unshift(e):Gn.push(e)}});function nr(e,t,n){var r,i,o,a,s,l,u=this,c={},p=e.style,f=e.nodeType&&nn(e),d=x._data(e,"fxshow");n.queue||(s=x._queueHooks(e,"fx"),null==s.unqueued&&(s.unqueued=0,l=s.empty.fire,s.empty.fire=function(){s.unqueued||l()}),s.unqueued++,u.always(function(){u.always(function(){s.unqueued--,x.queue(e,"fx").length||s.empty.fire()})})),1===e.nodeType&&("height"in t||"width"in t)&&(n.overflow=[p.overflow,p.overflowX,p.overflowY],"inline"===x.css(e,"display")&&"none"===x.css(e,"float")&&(x.support.inlineBlockNeedsLayout&&"inline"!==ln(e.nodeName)?p.zoom=1:p.display="inline-block")),n.overflow&&(p.overflow="hidden",x.support.shrinkWrapBlocks||u.always(function(){p.overflow=n.overflow[0],p.overflowX=n.overflow[1],p.overflowY=n.overflow[2]}));for(r in t)if(i=t[r],Vn.exec(i)){if(delete t[r],o=o||"toggle"===i,i===(f?"hide":"show"))continue;c[r]=d&&d[r]||x.style(e,r)}if(!x.isEmptyObject(c)){d?"hidden"in d&&(f=d.hidden):d=x._data(e,"fxshow",{}),o&&(d.hidden=!f),f?x(e).show():u.done(function(){x(e).hide()}),u.done(function(){var t;x._removeData(e,"fxshow");for(t in c)x.style(e,t,c[t])});for(r in c)a=Zn(f?d[r]:0,r,u),r in d||(d[r]=a.start,f&&(a.end=a.start,a.start="width"===r||"height"===r?1:0))}}function rr(e,t,n,r,i){return new rr.prototype.init(e,t,n,r,i)}x.Tween=rr,rr.prototype={constructor:rr,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||"swing",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(x.cssNumber[n]?"":"px")},cur:function(){var e=rr.propHooks[this.prop];return e&&e.get?e.get(this):rr.propHooks._default.get(this)},run:function(e){var t,n=rr.propHooks[this.prop];return this.pos=t=this.options.duration?x.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):rr.propHooks._default.set(this),this}},rr.prototype.init.prototype=rr.prototype,rr.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=x.css(e.elem,e.prop,""),t&&"auto"!==t?t:0):e.elem[e.prop]},set:function(e){x.fx.step[e.prop]?x.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[x.cssProps[e.prop]]||x.cssHooks[e.prop])?x.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},rr.propHooks.scrollTop=rr.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},x.each(["toggle","show","hide"],function(e,t){var n=x.fn[t];x.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(ir(t,!0),e,r,i)}}),x.fn.extend({fadeTo:function(e,t,n,r){return this.filter(nn).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=x.isEmptyObject(e),o=x.speed(t,n,r),a=function(){var t=er(this,x.extend({},e),o);(i||x._data(this,"finish"))&&t.stop(!0)};return a.finish=a,i||o.queue===!1?this.each(a):this.queue(o.queue,a)},stop:function(e,n,r){var i=function(e){var t=e.stop;delete e.stop,t(r)};return"string"!=typeof e&&(r=n,n=e,e=t),n&&e!==!1&&this.queue(e||"fx",[]),this.each(function(){var t=!0,n=null!=e&&e+"queueHooks",o=x.timers,a=x._data(this);if(n)a[n]&&a[n].stop&&i(a[n]);else for(n in a)a[n]&&a[n].stop&&Jn.test(n)&&i(a[n]);for(n=o.length;n--;)o[n].elem!==this||null!=e&&o[n].queue!==e||(o[n].anim.stop(r),t=!1,o.splice(n,1));(t||!r)&&x.dequeue(this,e)})},finish:function(e){return e!==!1&&(e=e||"fx"),this.each(function(){var t,n=x._data(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=x.timers,a=r?r.length:0;for(n.finish=!0,x.queue(this,e,[]),i&&i.stop&&i.stop.call(this,!0),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;a>t;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}});function ir(e,t){var n,r={height:e},i=0;for(t=t?1:0;4>i;i+=2-t)n=Zt[i],r["margin"+n]=r["padding"+n]=e;return t&&(r.opacity=r.width=e),r}x.each({slideDown:ir("show"),slideUp:ir("hide"),slideToggle:ir("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){x.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),x.speed=function(e,t,n){var r=e&&"object"==typeof e?x.extend({},e):{complete:n||!n&&t||x.isFunction(e)&&e,duration:e,easing:n&&t||t&&!x.isFunction(t)&&t};return r.duration=x.fx.off?0:"number"==typeof r.duration?r.duration:r.duration in x.fx.speeds?x.fx.speeds[r.duration]:x.fx.speeds._default,(null==r.queue||r.queue===!0)&&(r.queue="fx"),r.old=r.complete,r.complete=function(){x.isFunction(r.old)&&r.old.call(this),r.queue&&x.dequeue(this,r.queue)},r},x.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},x.timers=[],x.fx=rr.prototype.init,x.fx.tick=function(){var e,n=x.timers,r=0;for(Xn=x.now();n.length>r;r++)e=n[r],e()||n[r]!==e||n.splice(r--,1);n.length||x.fx.stop(),Xn=t},x.fx.timer=function(e){e()&&x.timers.push(e)&&x.fx.start()},x.fx.interval=13,x.fx.start=function(){Un||(Un=setInterval(x.fx.tick,x.fx.interval))},x.fx.stop=function(){clearInterval(Un),Un=null},x.fx.speeds={slow:600,fast:200,_default:400},x.fx.step={},x.expr&&x.expr.filters&&(x.expr.filters.animated=function(e){return x.grep(x.timers,function(t){return e===t.elem}).length}),x.fn.offset=function(e){if(arguments.length)return e===t?this:this.each(function(t){x.offset.setOffset(this,e,t)});var n,r,o={top:0,left:0},a=this[0],s=a&&a.ownerDocument;if(s)return n=s.documentElement,x.contains(n,a)?(typeof a.getBoundingClientRect!==i&&(o=a.getBoundingClientRect()),r=or(s),{top:o.top+(r.pageYOffset||n.scrollTop)-(n.clientTop||0),left:o.left+(r.pageXOffset||n.scrollLeft)-(n.clientLeft||0)}):o},x.offset={setOffset:function(e,t,n){var r=x.css(e,"position");"static"===r&&(e.style.position="relative");var i=x(e),o=i.offset(),a=x.css(e,"top"),s=x.css(e,"left"),l=("absolute"===r||"fixed"===r)&&x.inArray("auto",[a,s])>-1,u={},c={},p,f;l?(c=i.position(),p=c.top,f=c.left):(p=parseFloat(a)||0,f=parseFloat(s)||0),x.isFunction(t)&&(t=t.call(e,n,o)),null!=t.top&&(u.top=t.top-o.top+p),null!=t.left&&(u.left=t.left-o.left+f),"using"in t?t.using.call(e,u):i.css(u)}},x.fn.extend({position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return"fixed"===x.css(r,"position")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),x.nodeName(e[0],"html")||(n=e.offset()),n.top+=x.css(e[0],"borderTopWidth",!0),n.left+=x.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-x.css(r,"marginTop",!0),left:t.left-n.left-x.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||s;while(e&&!x.nodeName(e,"html")&&"static"===x.css(e,"position"))e=e.offsetParent;return e||s})}}),x.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);x.fn[e]=function(i){return x.access(this,function(e,i,o){var a=or(e);return o===t?a?n in a?a[n]:a.document.documentElement[i]:e[i]:(a?a.scrollTo(r?x(a).scrollLeft():o,r?o:x(a).scrollTop()):e[i]=o,t)},e,i,arguments.length,null)}});function or(e){return x.isWindow(e)?e:9===e.nodeType?e.defaultView||e.parentWindow:!1}x.each({Height:"height",Width:"width"},function(e,n){x.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){x.fn[i]=function(i,o){var a=arguments.length&&(r||"boolean"!=typeof i),s=r||(i===!0||o===!0?"margin":"border");return x.access(this,function(n,r,i){var o;return x.isWindow(n)?n.document.documentElement["client"+e]:9===n.nodeType?(o=n.documentElement,Math.max(n.body["scroll"+e],o["scroll"+e],n.body["offset"+e],o["offset"+e],o["client"+e])):i===t?x.css(n,r,s):x.style(n,r,i,s)},n,a?i:t,a,null)}})}),x.fn.size=function(){return this.length},x.fn.andSelf=x.fn.addBack,"object"==typeof module&&module&&"object"==typeof module.exports?module.exports=x:(e.jQuery=e.$=x,"function"==typeof define&&define.amd&&define("jquery",[],function(){return x}))})(window);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ribo_tools/lightbox/js/lightbox-2.6.min.js Wed Sep 27 04:59:10 2017 -0400 @@ -0,0 +1,1 @@ +(function(){var b,d,c;b=jQuery;c=(function(){function b(){this.fadeDuration=500;this.fitImagesInViewport=true;this.resizeDuration=700;this.showImageNumberLabel=true;this.wrapAround=false}b.prototype.albumLabel=function(b,c){return"Image "+b+" of "+c};return b})();d=(function(){function c(b){this.options=b;this.album=[];this.currentImageIndex=void 0;this.init()}c.prototype.init=function(){this.enable();return this.build()};c.prototype.enable=function(){var c=this;return b('body').on('click','a[rel^=lightbox], area[rel^=lightbox], a[data-lightbox], area[data-lightbox]',function(d){c.start(b(d.currentTarget));return false})};c.prototype.build=function(){var c=this;b("<div id='lightboxOverlay' class='lightboxOverlay'></div><div id='lightbox' class='lightbox'><div class='lb-outerContainer'><div class='lb-container'><img class='lb-image' src='' /><div class='lb-nav'><a class='lb-prev' href='' ></a><a class='lb-next' href='' ></a></div><div class='lb-loader'><a class='lb-cancel'></a></div></div></div><div class='lb-dataContainer'><div class='lb-data'><div class='lb-details'><span class='lb-caption'></span><span class='lb-number'></span></div><div class='lb-closeContainer'><a class='lb-close'></a></div></div></div></div>").appendTo(b('body'));this.$lightbox=b('#lightbox');this.$overlay=b('#lightboxOverlay');this.$outerContainer=this.$lightbox.find('.lb-outerContainer');this.$container=this.$lightbox.find('.lb-container');this.containerTopPadding=parseInt(this.$container.css('padding-top'),10);this.containerRightPadding=parseInt(this.$container.css('padding-right'),10);this.containerBottomPadding=parseInt(this.$container.css('padding-bottom'),10);this.containerLeftPadding=parseInt(this.$container.css('padding-left'),10);this.$overlay.hide().on('click',function(){c.end();return false});this.$lightbox.hide().on('click',function(d){if(b(d.target).attr('id')==='lightbox'){c.end()}return false});this.$outerContainer.on('click',function(d){if(b(d.target).attr('id')==='lightbox'){c.end()}return false});this.$lightbox.find('.lb-prev').on('click',function(){if(c.currentImageIndex===0){c.changeImage(c.album.length-1)}else{c.changeImage(c.currentImageIndex-1)}return false});this.$lightbox.find('.lb-next').on('click',function(){if(c.currentImageIndex===c.album.length-1){c.changeImage(0)}else{c.changeImage(c.currentImageIndex+1)}return false});return this.$lightbox.find('.lb-loader, .lb-close').on('click',function(){c.end();return false})};c.prototype.start=function(c){var f,e,j,d,g,n,o,k,l,m,p,h,i;b(window).on("resize",this.sizeOverlay);b('select, object, embed').css({visibility:"hidden"});this.$overlay.width(b(document).width()).height(b(document).height()).fadeIn(this.options.fadeDuration);this.album=[];g=0;j=c.attr('data-lightbox');if(j){h=b(c.prop("tagName")+'[data-lightbox="'+j+'"]');for(d=k=0,m=h.length;k<m;d=++k){e=h[d];this.album.push({link:b(e).attr('href'),title:b(e).attr('title')});if(b(e).attr('href')===c.attr('href')){g=d}}}else{if(c.attr('rel')==='lightbox'){this.album.push({link:c.attr('href'),title:c.attr('title')})}else{i=b(c.prop("tagName")+'[rel="'+c.attr('rel')+'"]');for(d=l=0,p=i.length;l<p;d=++l){e=i[d];this.album.push({link:b(e).attr('href'),title:b(e).attr('title')});if(b(e).attr('href')===c.attr('href')){g=d}}}}f=b(window);o=f.scrollTop()+f.height()/10;n=f.scrollLeft();this.$lightbox.css({top:o+'px',left:n+'px'}).fadeIn(this.options.fadeDuration);this.changeImage(g)};c.prototype.changeImage=function(f){var d,c,e=this;this.disableKeyboardNav();d=this.$lightbox.find('.lb-image');this.sizeOverlay();this.$overlay.fadeIn(this.options.fadeDuration);b('.lb-loader').fadeIn('slow');this.$lightbox.find('.lb-image, .lb-nav, .lb-prev, .lb-next, .lb-dataContainer, .lb-numbers, .lb-caption').hide();this.$outerContainer.addClass('animating');c=new Image();c.onload=function(){var m,g,h,i,j,k,l;d.attr('src',e.album[f].link);m=b(c);d.width(c.width);d.height(c.height);if(e.options.fitImagesInViewport){l=b(window).width();k=b(window).height();j=l-e.containerLeftPadding-e.containerRightPadding-20;i=k-e.containerTopPadding-e.containerBottomPadding-110;if((c.width>j)||(c.height>i)){if((c.width/j)>(c.height/i)){h=j;g=parseInt(c.height/(c.width/h),10);d.width(h);d.height(g)}else{g=i;h=parseInt(c.width/(c.height/g),10);d.width(h);d.height(g)}}}return e.sizeContainer(d.width(),d.height())};c.src=this.album[f].link;this.currentImageIndex=f};c.prototype.sizeOverlay=function(){return b('#lightboxOverlay').width(b(document).width()).height(b(document).height())};c.prototype.sizeContainer=function(f,g){var b,d,e,h,c=this;h=this.$outerContainer.outerWidth();e=this.$outerContainer.outerHeight();d=f+this.containerLeftPadding+this.containerRightPadding;b=g+this.containerTopPadding+this.containerBottomPadding;this.$outerContainer.animate({width:d,height:b},this.options.resizeDuration,'swing');setTimeout(function(){c.$lightbox.find('.lb-dataContainer').width(d);c.$lightbox.find('.lb-prevLink').height(b);c.$lightbox.find('.lb-nextLink').height(b);c.showImage()},this.options.resizeDuration)};c.prototype.showImage=function(){this.$lightbox.find('.lb-loader').hide();this.$lightbox.find('.lb-image').fadeIn('slow');this.updateNav();this.updateDetails();this.preloadNeighboringImages();this.enableKeyboardNav()};c.prototype.updateNav=function(){this.$lightbox.find('.lb-nav').show();if(this.album.length>1){if(this.options.wrapAround){this.$lightbox.find('.lb-prev, .lb-next').show()}else{if(this.currentImageIndex>0){this.$lightbox.find('.lb-prev').show()}if(this.currentImageIndex<this.album.length-1){this.$lightbox.find('.lb-next').show()}}}};c.prototype.updateDetails=function(){var b=this;if(typeof this.album[this.currentImageIndex].title!=='undefined'&&this.album[this.currentImageIndex].title!==""){this.$lightbox.find('.lb-caption').html(this.album[this.currentImageIndex].title).fadeIn('fast')}if(this.album.length>1&&this.options.showImageNumberLabel){this.$lightbox.find('.lb-number').text(this.options.albumLabel(this.currentImageIndex+1,this.album.length)).fadeIn('fast')}else{this.$lightbox.find('.lb-number').hide()}this.$outerContainer.removeClass('animating');this.$lightbox.find('.lb-dataContainer').fadeIn(this.resizeDuration,function(){return b.sizeOverlay()})};c.prototype.preloadNeighboringImages=function(){var c,b;if(this.album.length>this.currentImageIndex+1){c=new Image();c.src=this.album[this.currentImageIndex+1].link}if(this.currentImageIndex>0){b=new Image();b.src=this.album[this.currentImageIndex-1].link}};c.prototype.enableKeyboardNav=function(){b(document).on('keyup.keyboard',b.proxy(this.keyboardAction,this))};c.prototype.disableKeyboardNav=function(){b(document).off('.keyboard')};c.prototype.keyboardAction=function(g){var d,e,f,c,b;d=27;e=37;f=39;b=g.keyCode;c=String.fromCharCode(b).toLowerCase();if(b===d||c.match(/x|o|c/)){this.end()}else if(c==='p'||b===e){if(this.currentImageIndex!==0){this.changeImage(this.currentImageIndex-1)}}else if(c==='n'||b===f){if(this.currentImageIndex!==this.album.length-1){this.changeImage(this.currentImageIndex+1)}}};c.prototype.end=function(){this.disableKeyboardNav();b(window).off("resize",this.sizeOverlay);this.$lightbox.fadeOut(this.options.fadeDuration);this.$overlay.fadeOut(this.options.fadeDuration);return b('select, object, embed').css({visibility:"visible"})};return c})();b(function(){var e,b;b=new c();return e=new d(b)})}).call(this); \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ribo_tools/lightbox/js/modernizr.custom.js Wed Sep 27 04:59:10 2017 -0400 @@ -0,0 +1,4 @@ +/* Modernizr 2.6.2 (Custom Build) | MIT & BSD + * Build: http://modernizr.com/download/#-shiv-cssclasses-teststyles-testprop-testallprops-hasevent-prefixes-domprefixes + */ +;window.Modernizr=function(a,b,c){function A(a){j.cssText=a}function B(a,b){return A(m.join(a+";")+(b||""))}function C(a,b){return typeof a===b}function D(a,b){return!!~(""+a).indexOf(b)}function E(a,b){for(var d in a){var e=a[d];if(!D(e,"-")&&j[e]!==c)return b=="pfx"?e:!0}return!1}function F(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:C(f,"function")?f.bind(d||b):f}return!1}function G(a,b,c){var d=a.charAt(0).toUpperCase()+a.slice(1),e=(a+" "+o.join(d+" ")+d).split(" ");return C(b,"string")||C(b,"undefined")?E(e,b):(e=(a+" "+p.join(d+" ")+d).split(" "),F(e,b,c))}var d="2.6.2",e={},f=!0,g=b.documentElement,h="modernizr",i=b.createElement(h),j=i.style,k,l={}.toString,m=" -webkit- -moz- -o- -ms- ".split(" "),n="Webkit Moz O ms",o=n.split(" "),p=n.toLowerCase().split(" "),q={},r={},s={},t=[],u=t.slice,v,w=function(a,c,d,e){var f,i,j,k,l=b.createElement("div"),m=b.body,n=m||b.createElement("body");if(parseInt(d,10))while(d--)j=b.createElement("div"),j.id=e?e[d]:h+(d+1),l.appendChild(j);return f=["­",'<style id="s',h,'">',a,"</style>"].join(""),l.id=h,(m?l:n).innerHTML+=f,n.appendChild(l),m||(n.style.background="",n.style.overflow="hidden",k=g.style.overflow,g.style.overflow="hidden",g.appendChild(n)),i=c(l,a),m?l.parentNode.removeChild(l):(n.parentNode.removeChild(n),g.style.overflow=k),!!i},x=function(){function d(d,e){e=e||b.createElement(a[d]||"div"),d="on"+d;var f=d in e;return f||(e.setAttribute||(e=b.createElement("div")),e.setAttribute&&e.removeAttribute&&(e.setAttribute(d,""),f=C(e[d],"function"),C(e[d],"undefined")||(e[d]=c),e.removeAttribute(d))),e=null,f}var a={select:"input",change:"input",submit:"form",reset:"form",error:"img",load:"img",abort:"img"};return d}(),y={}.hasOwnProperty,z;!C(y,"undefined")&&!C(y.call,"undefined")?z=function(a,b){return y.call(a,b)}:z=function(a,b){return b in a&&C(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=u.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(u.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(u.call(arguments)))};return e});for(var H in q)z(q,H)&&(v=H.toLowerCase(),e[v]=q[H](),t.push((e[v]?"":"no-")+v));return e.addTest=function(a,b){if(typeof a=="object")for(var d in a)z(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return e;b=typeof b=="function"?b():b,typeof f!="undefined"&&f&&(g.className+=" "+(b?"":"no-")+a),e[a]=b}return e},A(""),i=k=null,function(a,b){function k(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x<style>"+b+"</style>",d.insertBefore(c.lastChild,d.firstChild)}function l(){var a=r.elements;return typeof a=="string"?a.split(" "):a}function m(a){var b=i[a[g]];return b||(b={},h++,a[g]=h,i[h]=b),b}function n(a,c,f){c||(c=b);if(j)return c.createElement(a);f||(f=m(c));var g;return f.cache[a]?g=f.cache[a].cloneNode():e.test(a)?g=(f.cache[a]=f.createElem(a)).cloneNode():g=f.createElem(a),g.canHaveChildren&&!d.test(a)?f.frag.appendChild(g):g}function o(a,c){a||(a=b);if(j)return a.createDocumentFragment();c=c||m(a);var d=c.frag.cloneNode(),e=0,f=l(),g=f.length;for(;e<g;e++)d.createElement(f[e]);return d}function p(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return r.shivMethods?n(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+l().join().replace(/\w+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(r,b.frag)}function q(a){a||(a=b);var c=m(a);return r.shivCSS&&!f&&!c.hasCSS&&(c.hasCSS=!!k(a,"article,aside,figcaption,figure,footer,header,hgroup,nav,section{display:block}mark{background:#FF0;color:#000}")),j||p(a,c),a}var c=a.html5||{},d=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,e=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,f,g="_html5shiv",h=0,i={},j;(function(){try{var a=b.createElement("a");a.innerHTML="<xyz></xyz>",f="hidden"in a,j=a.childNodes.length==1||function(){b.createElement("a");var a=b.createDocumentFragment();return typeof a.cloneNode=="undefined"||typeof a.createDocumentFragment=="undefined"||typeof a.createElement=="undefined"}()}catch(c){f=!0,j=!0}})();var r={elements:c.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",shivCSS:c.shivCSS!==!1,supportsUnknownElements:j,shivMethods:c.shivMethods!==!1,type:"default",shivDocument:q,createElement:n,createDocumentFragment:o};a.html5=r,q(b)}(this,b),e._version=d,e._prefixes=m,e._domPrefixes=p,e._cssomPrefixes=o,e.hasEvent=x,e.testProp=function(a){return E([a])},e.testAllProps=G,e.testStyles=w,g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(f?" js "+t.join(" "):""),e}(this,this.document); \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ribo_tools/lightbox/js/stupidtable.js Wed Sep 27 04:59:10 2017 -0400 @@ -0,0 +1,152 @@ +// Stupid jQuery table plugin. + +// Call on a table +// sortFns: Sort functions for your datatypes. +(function($) { + + $.fn.stupidtable = function(sortFns) { + return this.each(function() { + var $table = $(this); + sortFns = sortFns || {}; + + // ==================================================== // + // Utility functions // + // ==================================================== // + + // Merge sort functions with some default sort functions. + sortFns = $.extend({}, $.fn.stupidtable.default_sort_fns, sortFns); + + // Return the resulting indexes of a sort so we can apply + // this result elsewhere. This returns an array of index numbers. + // return[0] = x means "arr's 0th element is now at x" + var sort_map = function(arr, sort_function) { + var map = []; + var index = 0; + var sorted = arr.slice(0).sort(sort_function); + for (var i=0; i<arr.length; i++) { + index = $.inArray(arr[i], sorted); + + // If this index is already in the map, look for the next index. + // This handles the case of duplicate entries. + while ($.inArray(index, map) != -1) { + index++; + } + map.push(index); + } + + return map; + }; + + // Apply a sort map to the array. + var apply_sort_map = function(arr, map) { + var clone = arr.slice(0), + newIndex = 0; + for (var i=0; i<map.length; i++) { + newIndex = map[i]; + clone[newIndex] = arr[i]; + } + return clone; + }; + + // ==================================================== // + // Begin execution! // + // ==================================================== // + + // Do sorting when THs are clicked + $table.on("click", "th", function() { + var trs = $table.children("tbody").children("tr"); + var $this = $(this); + var th_index = 0; + var dir = $.fn.stupidtable.dir; + + $table.find("th").slice(0, $this.index()).each(function() { + var cols = $(this).attr("colspan") || 1; + th_index += parseInt(cols,10); + }); + + // Determine (and/or reverse) sorting direction, default `asc` + var sort_dir = $this.data("sort-default") || dir.ASC; + if ($this.data("sort-dir")) + sort_dir = $this.data("sort-dir") === dir.ASC ? dir.DESC : dir.ASC; + + // Choose appropriate sorting function. + var type = $this.data("sort") || null; + + // Prevent sorting if no type defined + if (type === null) { + return; + } + + // Trigger `beforetablesort` event that calling scripts can hook into; + // pass parameters for sorted column index and sorting direction + $table.trigger("beforetablesort", {column: th_index, direction: sort_dir}); + // More reliable method of forcing a redraw + $table.css("display"); + + // Run sorting asynchronously on a timout to force browser redraw after + // `beforetablesort` callback. Also avoids locking up the browser too much. + setTimeout(function() { + // Gather the elements for this column + var column = []; + var sortMethod = sortFns[type]; + + // Push either the value of the `data-order-by` attribute if specified + // or just the text() value in this column to column[] for comparison. + trs.each(function(index,tr) { + var $e = $(tr).children().eq(th_index); + var sort_val = $e.data("sort-value"); + var order_by = typeof(sort_val) !== "undefined" ? sort_val : $e.text(); + column.push(order_by); + }); + + // Create the sort map. This column having a sort-dir implies it was + // the last column sorted. As long as no data-sort-desc is specified, + // we're free to just reverse the column. + var theMap; + if (sort_dir == dir.ASC) + theMap = sort_map(column, sortMethod); + else + theMap = sort_map(column, function(a, b) { return -sortMethod(a, b); }); + + // Reset siblings + $table.find("th").data("sort-dir", null).removeClass("sorting-desc sorting-asc"); + $this.data("sort-dir", sort_dir).addClass("sorting-"+sort_dir); + + var sortedTRs = $(apply_sort_map(trs, theMap)); + $table.children("tbody").remove(); + $table.append("<tbody />").append(sortedTRs); + + // Trigger `aftertablesort` event. Similar to `beforetablesort` + $table.trigger("aftertablesort", {column: th_index, direction: sort_dir}); + // More reliable method of forcing a redraw + $table.css("display"); + }, 10); + }); + }); + }; + + // Enum containing sorting directions + $.fn.stupidtable.dir = {ASC: "asc", DESC: "desc"}; + + $.fn.stupidtable.default_sort_fns = { + "int": function(a, b) { + return parseInt(a, 10) - parseInt(b, 10); + }, + "float": function(a, b) { + return parseFloat(a) - parseFloat(b); + }, + "string": function(a, b) { + if (a < b) return -1; + if (a > b) return +1; + return 0; + }, + "string-ins": function(a, b) { + a = a.toLowerCase(); + b = b.toLowerCase(); + if (a < b) return -1; + if (a > b) return +1; + return 0; + } + }; + +})(jQuery);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ribo_tools/lightbox/js/stupidtable.min.js Wed Sep 27 04:59:10 2017 -0400 @@ -0,0 +1,1 @@ +(function(d){d.fn.stupidtable=function(b){return this.each(function(){var a=d(this);b=b||{};b=d.extend({},d.fn.stupidtable.default_sort_fns,b);var n=function(a,b){for(var f=[],c=0,e=a.slice(0).sort(b),h=0;h<a.length;h++){for(c=d.inArray(a[h],e);-1!=d.inArray(c,f);)c++;f.push(c)}return f},q=function(a,b){for(var d=a.slice(0),c=0,e=0;e<b.length;e++)c=b[e],d[c]=a[e];return d};a.on("click","th",function(){var m=a.children("tbody").children("tr"),g=d(this),f=0,c=d.fn.stupidtable.dir;a.find("th").slice(0, g.index()).each(function(){var a=d(this).attr("colspan")||1;f+=parseInt(a,10)});var e=g.data("sort-default")||c.ASC;g.data("sort-dir")&&(e=g.data("sort-dir")===c.ASC?c.DESC:c.ASC);var h=g.data("sort")||null;null!==h&&(a.trigger("beforetablesort",{column:f,direction:e}),a.css("display"),setTimeout(function(){var l=[],p=b[h];m.each(function(a,b){var c=d(b).children().eq(f),e=c.data("sort-value"),c="undefined"!==typeof e?e:c.text();l.push(c)});var k;k=e==c.ASC?n(l,p):n(l,function(a,b){return-p(a,b)}); a.find("th").data("sort-dir",null).removeClass("sorting-desc sorting-asc");g.data("sort-dir",e).addClass("sorting-"+e);k=d(q(m,k));a.children("tbody").remove();a.append("<tbody />").append(k);a.trigger("aftertablesort",{column:f,direction:e});a.css("display")},10))})})};d.fn.stupidtable.dir={ASC:"asc",DESC:"desc"};d.fn.stupidtable.default_sort_fns={"int":function(b,a){return parseInt(b,10)-parseInt(a,10)},"float":function(b,a){return parseFloat(b)-parseFloat(a)},string:function(b,a){return b<a?-1: b>a?1:0},"string-ins":function(b,a){b=b.toLowerCase();a=a.toLowerCase();return b<a?-1:b>a?1:0}}})(jQuery);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ribo_tools/metagene_frameshift_analysis.py Wed Sep 27 04:59:10 2017 -0400 @@ -0,0 +1,880 @@ +#!/usr/bin/env python2.7 +#-*- coding: utf-8 -*- +''' + Created on sep. 2013 + @author: rachel legendre + @copyright: rachel.legendre@igmors.u-psud.fr + @license: GPL v3 +''' + + +import os, sys, optparse, tempfile, subprocess, re, shutil, commands, urllib, time, csv +import math +from numpy import arange +#from matplotlib import pyplot as pl +import matplotlib +matplotlib.use('Agg') +import matplotlib.pyplot as pl +import itertools +from Bio import SeqIO +from Bio.Seq import Seq +from Bio.Alphabet import IUPAC +from PIL import Image +import ribo_functions +##libraries for debugg +#import cPickle +#import pdb + + +##Setting +MIN_COV_REQUIREMENT = 500 +total_mapped_read = 0 +_MAX = 30 +whole_phasing_table = [[],[],[]] + + +def stop_err( msg ): + sys.stderr.write( "%s\n" % msg ) + sys.stderr.write( "Programme aborted at %s\n" % time.asctime(time.localtime(time.time()))) + sys.exit() + + +def split_bam(bamfile,tmpdir): + ''' + split bam by chromosome and write sam file in tmpdir + ''' + try: + #get header + results = subprocess.check_output(['samtools', 'view', '-H',bamfile]) + header = results.split('\n') + + #define genome size + genome = [] + for line in header: + result = re.search('SN', line) + if result : + #print line + feat = line.split('\t') + chrom = re.split(":", feat[1]) + #print feat[1] + genome.append(chrom[1]) + + #split sam by chrom + n = 0 + for chrm in genome: + with open(tmpdir+'/'+chrm+'.sam', 'w') as f : + #write header correctly for each chromosome + f.write(header[0]+'\n') + expr = re.compile(chrm+'\t') + el =[elem for elem in header if expr.search(elem)][0] + f.write(el+'\n') + f.write(header[-2]+'\n') + #write all reads for each chromosome + reads = subprocess.check_output(["samtools", "view", bamfile, chrm]) + f.write(reads) + # calculate number of reads + n += reads.count(chrm) + + sys.stdout.write("%d reads are present in your bam file\n" % n) + + except Exception, e: + stop_err( 'Error during bam file splitting : ' + str( e ) ) + + + +def get_first_base(tmpdir, kmer, frame): + ''' + write footprint coverage file for each sam file in tmpdir + ''' + global total_mapped_read + ## tags by default + multi_tag = "XS:i:" + tag = "IH:i:1" + + try: + ### compute position of P-site according to frame (P-site -> +15 starting from 5' end of footprint) + p_site_pos = 16-frame + + file_array = (commands.getoutput('ls '+tmpdir)).split('\n') + ##write coverage for each sam file in tmpdir + for samfile in file_array: + with open(tmpdir+'/'+samfile, 'r') as sam : + ##get chromosome name + chrom = samfile.split(".sam")[0] + + for line in sam: + #initialize dictionnary + if '@SQ' in line : + size = int(line.split('LN:')[1]) + genomeF = [0]*size + genomeR = [0]*size + # define multiple reads keys from mapper + elif '@PG\tID' in line : + if 'bowtie' in line: + multi_tag = "XS:i:" + elif 'bwa' in line: + multi_tag = "XT:A:R" + elif 'TopHat' in line: + tag = "NH:i:1" + else : + stop_err("No PG tag find in "+samfile+". Please use bowtie, bwa or Tophat for mapping") + + # get footprint + elif re.search('^[^@].+', line) : + #print line.rstrip() + read_pos = int(line.split('\t')[3]) + read_sens = int(line.split('\t')[1]) + len_read = len(line.split('\t')[9]) + read_qual = int(line.split('\t')[4]) + if len_read == kmer and (tag in line or multi_tag not in line) and read_qual > 20 : + ###if line.split('\t')[5] == '28M' : + # print line.rstrip() + total_mapped_read +=1 + #if it's a forward read + if read_sens == 0 : + #get P-site : start read + 12 nt + #read_pos += 15 + read_pos += p_site_pos + genomeF[read_pos] += 1 + #if it's a reverse read + elif read_sens == 16 : + #get P-site + #read_pos += 12 + read_pos += (len_read-1) - p_site_pos + genomeR[read_pos] += 1 + + #try: + #write coverage in files + with open(tmpdir+'/assoCov_'+chrom+'.txt', 'w') as cov : + for i in range(0,size): + cov.write(str(genomeF[i])+'\t-'+str(genomeR[i])+'\n') + #except Exception,e: + # stop_err( 'Error during coverage file writting : ' + str( e ) ) + del genomeF + del genomeR + sys.stdout.write("%d reads are used for frame analysis\n" % total_mapped_read) + except Exception, e: + stop_err( 'Error during footprinting : ' + str( e ) ) + + +def __percent__(prop): + + if sum(prop) != 0 : + perc = [0,0,0] + if prop[0] != 0 : + perc[0] = int("{0:.0f}".format((prop[0]*100.0)/sum(prop))) + if prop[1] != 0 : + perc[1] = int("{0:.0f}".format((prop[1]*100.0)/sum(prop))) + if prop[2] != 0 : + perc[2] = int("{0:.0f}".format((prop[2]*100.0)/sum(prop))) + return perc + else : + return prop + + +def frame_analysis(tmpdir,subfolder,cutoff,GFF,box_file, orf_length): + ''' + This function take footprint and annotation (gff) for analyse reading frame in each gene + ''' + global total_mapped_read + global whole_phasing_table + try: + chrom = "" # initializing chromosome + nb_gene = 0 # number of analysed genes + mean_orf = 0 + myheader = ['Gene','Name','Total_proportion','Dual_coding','RPKM','Segment_RPKM','Note'] + + with open(subfolder+'/dual_coding.csv',"w") as out : + #out.write('Gene\tName\tTotal_proportion\tDual_coding\tRPKM\tSegment_RPKM\tNote\n') + writer = csv.writer(out,delimiter=',') + writer.writerow(myheader) + whole_phasing = [0,0,0] + for gene in GFF['order']: + cov = [] + ##first chromosome : we open corresponding file + try: + if chrom == "" : + chrom = GFF[gene]['chrom'] + with open(tmpdir+"/assoCov_"+chrom+".txt") as f : + data = f.readlines() + ##if we change chromosome + elif chrom != GFF[gene]['chrom'] : + chrom = GFF[gene]['chrom'] + with open(tmpdir+"/assoCov_"+chrom+".txt") as f : + data = f.readlines() + except IOError : + print tmpdir+"/assoCov_"+chrom+".txt doesn't exist" + + try : + ## if a gene without intron : + if GFF[gene]['exon_number'] == 1: + + ## get coverage for each gene + if GFF[gene]['strand'] == "+": + for i in range(GFF[gene]['exon'][1]['start'],GFF[gene]['exon'][1]['stop']+1): + cov.append(int((data[i].rstrip()).split("\t")[0])) + else : + for i in range(GFF[gene]['exon'][1]['start'],GFF[gene]['exon'][1]['stop']+1): + cov.append(int(((data[i].rstrip()).split("\t")[1]).replace("-",""))) + cov.reverse() + else : + ## For each gene, get coverage and sum of exon size + if GFF[gene]['strand'] == "+": + + for exon in range(1,GFF[gene]['exon_number']+1) : + for i in range(GFF[gene]['exon'][exon]['start'],GFF[gene]['exon'][exon]['stop']+1): + cov.append(int((data[i].rstrip()).split("\t")[0])) + else : + + for exon in range(1,GFF[gene]['exon_number']+1) : + for i in range(GFF[gene]['exon'][exon]['start'],GFF[gene]['exon'][exon]['stop']+1): + cov.append(int(((data[i].rstrip()).split("\t")[1]).replace("-",""))) + cov.reverse() + except : + print gene+" could not be analysed." + del GFF[gene] + continue + + len_cov = len(cov) + ## segmentation by orf_length + nb_segment = int(len_cov/orf_length) + ## too short segment gestion + if len_cov < orf_length : + nb_segment = 1 + + ## number of aa in gene : + nb_aa = len_cov/3 + len_frag = len_cov/nb_segment + prop = [0,0,0] + seg_prop = [] + tot_prop = prop + tot_rpkm = 0 + seg_rpkm = [] + window = 0 + GFF[gene]['dual_coding'] = '-' + ''' segmentation and total proportions ''' + if sum(cov) > MIN_COV_REQUIREMENT: + ## compute mean of ORF length + mean_orf += len_cov + nb_gene += 1 + for nuc in range(0,len_cov-2,3) : + window += 1 + tot_rpkm += sum(cov[nuc:nuc+2]) + ## Calculate proportion + prop[0] += cov[nuc] + prop[1] += cov[nuc+1] + prop[2] += cov[nuc+2] + #segmented proportion + if window == nb_aa/nb_segment: + seg_prop.append(__percent__(prop)) + whole_phasing = (map(lambda (x, y): x + y, zip(whole_phasing, prop))) + tot_prop = (map(lambda (x, y): x + y, zip(tot_prop, prop))) + seg_rpkm.append("{0:.2f}".format((tot_rpkm*1000000.0)/(total_mapped_read*len_frag))) + window = 0 + tot_rpkm = 0 + prop = [0,0,0] + + GFF[gene]['total_prop'] = __percent__(tot_prop) + GFF[gene]['seg_prop'] = seg_prop + GFF[gene]['seg_rpkm'] = seg_rpkm + tot_rpkm = "{0:.2f}".format(sum(cov)*1000000.0/(total_mapped_read*len_cov)) + GFF[gene]['total_rpkm'] = tot_rpkm + ## If gene has non standard proportion + idx = 0 + for seg in GFF[gene]['seg_prop'] : + if (seg[0] < cutoff) and (sum(seg) != 0 and GFF[gene]['seg_rpkm'][idx] != 0.0) : + GFF[gene]['dual_coding'] = 'yes' + idx += 1 + for z in range(0,3) : + whole_phasing_table[z].append(__percent__(tot_prop)[z]) +# else : +# for nuc in range(0,len_cov-2,3) : +# ## Calculate proportion +# prop[0] += cov[nuc] +# prop[1] += cov[nuc+1] +# prop[2] += cov[nuc+2] +# whole_phasing = (map(lambda (x, y): x + y, zip(whole_phasing, prop))) +# for z in range(0,3) : +# whole_phasing_table[z].append(__percent__(tot_prop)[z]) + + if GFF[gene]['dual_coding'] == 'yes' : + mylist = [gene,GFF[gene]['name'],GFF[gene]['total_prop'],GFF[gene]['seg_prop'],GFF[gene]['total_rpkm'],GFF[gene]['seg_rpkm'],GFF[gene]['note']] + #out.write(gene+'\t'+GFF[gene]['name']+'\t'+str(GFF[gene]['total_prop'])+'\t'+str(GFF[gene]['seg_prop'])+'\t'+str(GFF[gene]['total_rpkm'])+'\t'+GFF[gene]['note']+'\n') + writer.writerow(mylist) + GFF[gene]['coverage'] = cov + else : + del GFF[gene] + + del GFF['order'] + + whole_phasing = __percent__(whole_phasing) + sys.stdout.write("Proportion of reads in each frame :\n%s\n" % whole_phasing) + if nb_gene == 0: + sys.stdout.write("%d gene are used for frame analysis: each gene require %d reads at least to be analysed.\n" % (nb_gene, MIN_COV_REQUIREMENT)) + else : + sys.stdout.write("Mean length of ORF in your organism : %s\n" % int(mean_orf/nb_gene)) + sys.stdout.write("%d genes are used for frame analysis\n" % nb_gene) + + ##produce boxplot + pl.boxplot(whole_phasing_table,notch=True,sym='ro') + pl.ylabel('Total number of footprints (percent)') + pl.ylim(0,100) + pl.draw() + pl.savefig(subfolder+'/boxplot.png',format='png', dpi=640) + pl.savefig(box_file, format='png', dpi=640) + pl.clf() + + return GFF + + except Exception, e: + stop_err( 'Error during frame analysis : ' + str( e ) ) + +def get_orf(fasta,gene): + ''' + This function compute ORF in each gene and return multiple lists of methionines and stop in each frame + ''' + try : + SeqDict = SeqIO.to_dict(SeqIO.parse(open(fasta,"r"),"fasta")) + if gene['exon_number'] == 1 : + if gene['strand'] == '-' : + gene_seq = str(SeqDict[gene['chrom']].seq[gene['start']-2:gene['stop']+1].reverse_complement()) + else : + gene_seq = str(SeqDict[gene['chrom']].seq[gene['start']-2:gene['stop']+1]) + else : + ## get sequence of gene with intron with -1 and +1 nt for detect all phases + gene_seq = str(SeqDict[gene['chrom']].seq[gene['start']-2:gene['start']-1]) + for exon in range(1,gene['exon_number']+1) : + gene_seq = gene_seq+str(SeqDict[gene['chrom']].seq[gene['exon'][exon]['start']-1:gene['exon'][exon]['stop']]) + gene_seq = gene_seq+str(SeqDict[gene['chrom']].seq[gene['stop']:gene['stop']+1]) + if gene['strand'] == '-' : + my_seq = Seq(gene_seq,IUPAC.unambiguous_dna).reverse_complement() + #gene_seq = my_seq.tostring() DEPRECATED + gene_seq = str(my_seq) + # initialize array of start/stop array_1 is fame 0, array 2 is frame +1, array_3 is frame -1 + stop_1 = [] + stop_2 = [] + stop_3 = [] + met_1 = [] + met_2 = [] + met_3 = [] + stops = ['TAA','TGA','TAG'] + for z in range(0,len(gene_seq),3): + # Frame -1 + if gene_seq[z:z+3] == 'ATG' : + met_3.append(z) + elif gene_seq[z:z+3] in stops : + stop_3.append(z) + # Frame 0 + if gene_seq[z+1:z+4] == 'ATG' : + met_1.append(z+1) + elif gene_seq[z+1:z+4] in stops : + stop_1.append(z+1) + # Frame +1 + if gene_seq[z+2:z+5] == 'ATG' : + met_2.append(z+2) + elif gene_seq[z+2:z+5] in stops : + stop_2.append(z+2) + + + return gene_seq,met_1,stop_1,met_2,stop_2,met_3,stop_3 + + except Exception, e: + stop_err( 'Error during ORF computing : ' + str( e ) ) + + + +def plot_subprofile(GFF, directory,fasta) : + ''' + This function plot subprofiles of footprint and ORFs for each gene in GFF dict + ''' + global _MAX + try : + + for gene in GFF.iterkeys(): + if GFF[gene]['coverage'] : + nb_aa = len(GFF[gene]['coverage'])/3 + subprofile_1 = [] + subprofile_2 = [] + subprofile_3 = [] + cov = GFF[gene]['coverage'] + + + + for z in range(0,len(cov)-2,3): + for i in range(3): + subprofile_1.append(cov[z]) + subprofile_2.append(cov[z+1]) + subprofile_3.append(cov[z+2]) + + ## Normalisation of density plot + tot = max(cov) + + subprofile_1 = [(x * _MAX/tot) for x in subprofile_1] + subprofile_2 = [(x * _MAX/tot) for x in subprofile_2] + subprofile_3 = [(x * _MAX/tot) for x in subprofile_3] + + + if len(subprofile_1) == len(subprofile_2) and len(subprofile_1) == len(subprofile_3) : + + ind = arange(len(subprofile_1)) + fig = pl.figure(num=1) + #sub profile frame 0 + pl.subplot(4,1,1) + ax1 = fig.add_subplot(4,1,1) + pl.title( "%s (%s)\n$(%s RPKM)$"% (gene, GFF[gene]['name'], GFF[gene]['total_rpkm'])) + pl.ylim(0,_MAX) + pl.xlim(0,len(subprofile_1)) + pl.yticks([0, _MAX/2, _MAX]) + ax1.plot(ind, subprofile_1, 'b-', lw = 3,label = 'Frame 0') + ax1.fill_between(ind, 0,subprofile_1,color='b') + ax1.legend(loc=1,prop={'size':8}) + pl.ylabel('#RPF') + + ##sub profile frame +1 + pl.subplot(4,1,2) + ax2 = fig.add_subplot(4,1,2) + pl.ylim(0,_MAX) + pl.xlim(0,len(subprofile_2)) + pl.yticks([0, _MAX/2, _MAX]) + ax2.plot(ind,subprofile_2,'r-',lw = 3,label = 'Frame +1') + ax2.fill_between(ind, 0,subprofile_2,color='r') + ax2.legend(loc=1,prop={'size':8}) + pl.ylabel('#RPF') + + ##sub profile frame -1 + pl.subplot(4,1,3) + ax3 = fig.add_subplot(4,1,3) + pl.ylim(0,_MAX) + pl.yticks([0, _MAX/2, _MAX]) + pl.xlim(0,len(subprofile_3)) + ax3.plot(ind,subprofile_3,'g-',lw =3,label = 'Frame -1') + ax3.fill_between(ind, 0,subprofile_3,color='g') + ax3.legend(loc=1,prop={'size':8}) + pl.ylabel('#RPF') + + ## get orf for each frame + gene_seq,met_1,stop_1,met_2,stop_2,met_3,stop_3 = get_orf(fasta,GFF[gene]) + ## ORF plot + idc = arange(len(gene_seq)) + ##define axe for frame : + x = [0]*len(gene_seq) + y = [1]*len(gene_seq) + z = [2]*len(gene_seq) + ## define last subplot + #pdb.set_trace() + pl.subplot(4,1,4) + ## modify axes setting + ax4 = fig.add_subplot(4,1,4) + ax4.spines['right'].set_color('none') + ax4.spines['top'].set_color('none') + ax4.spines['left'].set_color('none') + ax4.spines['bottom'].set_color('none') + ax4.tick_params(bottom = 'off',top='off',left='off', right='off') + pl.ylim(0,3) + pl.xlim(0,len(gene_seq)) + pl.yticks([0.5,1.5,2.5],[r'-1',r'+1',r'0']) + pl.xticks([]) + ## plot line for delimiting frame and arrow corresponding to start and stop + pl.plot(idc,x,'k',lw=2) + for val in met_1: + pl.arrow( val, 2, 0.0, 0.5, fc="g", ec="g",lw=2) + for val in stop_1: + pl.arrow( val, 2, 0.0, 1, fc="r", ec="r",lw=2) + pl.plot(idc,y,'k') + for val in met_2: + pl.arrow( val, 1, 0.0, 0.5, fc="g", ec="g",lw=2) + for val in stop_2: + pl.arrow( val, 1, 0.0, 1, fc="r", ec="r",lw=2) + pl.plot(idc,z,'k') + for val in met_3: + pl.arrow( val, 0, 0.0, 0.5, fc="g", ec="g",lw=2) + for val in stop_3: + pl.arrow( val, 0, 0.0, 1, fc="r", ec="r",lw=2) + + + pl.ylabel('ORF',fontsize='small') + pl.draw() + pl.savefig(directory+'/'+gene+'.png',format='png', dpi=600) + pl.clf() + ##pl.show() + + ## Make thumbnail for html page + infile = directory+'/'+gene+'.png' + size = 128, 128 + im = Image.open(infile) + im.thumbnail(size, Image.ANTIALIAS) + im.save(directory+'/'+gene + "_thumbnail.png", "PNG") + + + else : + stop_err( 'Error during plot : lenght of subprofile are different ') + + except Exception, e: + stop_err( 'Error during subprofiles plotting : ' + str( e ) ) + +def write_html_page(html,subfolder,GFF) : + + + try : + if not GFF: + gene_table = 'Sorry, there are no dual coding events in your data\n' + else : + gene_table = '' + gene_table += '<table>' + gene_table += '<thead><tr><th data-sort="string">Gene</th><th>Plot</th><th data-sort="string">Name</th><th>Total Proportion</th><th>Segment proportion</th><th data-sort="float">RPKM</th><th>Segment RPKM</th></tr></thead><tbody>' + for gene in GFF.iterkeys(): + gene_table += '<tr><td>%s</td><td><a href="%s.png" data-lightbox="%s"><img src="%s_thumbnail.png" /></a></td><td><a title="%s">%s</a></td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>' %(gene,gene,gene,gene,GFF[gene]['note'],GFF[gene]['name'],GFF[gene]['total_prop'],GFF[gene]['seg_prop'],GFF[gene]['total_rpkm'],GFF[gene]['seg_rpkm']) + gene_table += '</tbody></table>' + + + html_str = """ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> + +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <link rel="stylesheet" href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" /> + <script src="http://code.jquery.com/jquery-1.10.2.min.js"></script> + <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> + <script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script> + <script src="lightbox/js/jquery-1.10.2.min.js"></script> + <script src="lightbox/js/lightbox-2.6.min.js"></script> + <link href="lightbox/css/lightbox.css" rel="stylesheet" /> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> + <title>Dual coding result file</title> + <link rel="stylesheet" href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" /> + <script> + (function(d){d.fn.stupidtable=function(b){return this.each(function(){var a=d(this);b=b||{};b=d.extend({},d.fn.stupidtable.default_sort_fns,b);var n=function(a,b){for(var f=[],c=0,e=a.slice(0).sort(b),h=0;h<a.length;h++){for(c=d.inArray(a[h],e);-1!=d.inArray(c,f);)c++;f.push(c)}return f},q=function(a,b){for(var d=a.slice(0),c=0,e=0;e<b.length;e++)c=b[e],d[c]=a[e];return d};a.on("click","th",function(){var m=a.children("tbody").children("tr"),g=d(this),f=0,c=d.fn.stupidtable.dir;a.find("th").slice(0, g.index()).each(function(){var a=d(this).attr("colspan")||1;f+=parseInt(a,10)});var e=g.data("sort-default")||c.ASC;g.data("sort-dir")&&(e=g.data("sort-dir")===c.ASC?c.DESC:c.ASC);var h=g.data("sort")||null;null!==h&&(a.trigger("beforetablesort",{column:f,direction:e}),a.css("display"),setTimeout(function(){var l=[],p=b[h];m.each(function(a,b){var c=d(b).children().eq(f),e=c.data("sort-value"),c="undefined"!==typeof e?e:c.text();l.push(c)});var k;k=e==c.ASC?n(l,p):n(l,function(a,b){return-p(a,b)}); a.find("th").data("sort-dir",null).removeClass("sorting-desc sorting-asc");g.data("sort-dir",e).addClass("sorting-"+e);k=d(q(m,k));a.children("tbody").remove();a.append("<tbody />").append(k);a.trigger("aftertablesort",{column:f,direction:e});a.css("display")},10))})})};d.fn.stupidtable.dir={ASC:"asc",DESC:"desc"};d.fn.stupidtable.default_sort_fns={"int":function(b,a){return parseInt(b,10)-parseInt(a,10)},"float":function(b,a){return parseFloat(b)-parseFloat(a)},string:function(b,a){return b<a?-1: b>a?1:0},"string-ins":function(b,a){b=b.toLowerCase();a=a.toLowerCase();return b<a?-1:b>a?1:0}}})(jQuery); + (function($) { + + $.fn.stupidtable = function(sortFns) { + return this.each(function() { + var $table = $(this); + sortFns = sortFns || {}; + + // ==================================================== // + // Utility functions // + // ==================================================== // + + // Merge sort functions with some default sort functions. + sortFns = $.extend({}, $.fn.stupidtable.default_sort_fns, sortFns); + + // Return the resulting indexes of a sort so we can apply + // this result elsewhere. This returns an array of index numbers. + // return[0] = x means "arr's 0th element is now at x" + var sort_map = function(arr, sort_function) { + var map = []; + var index = 0; + var sorted = arr.slice(0).sort(sort_function); + for (var i=0; i<arr.length; i++) { + index = $.inArray(arr[i], sorted); + + // If this index is already in the map, look for the next index. + // This handles the case of duplicate entries. + while ($.inArray(index, map) != -1) { + index++; + } + map.push(index); + } + + return map; + }; + + // Apply a sort map to the array. + var apply_sort_map = function(arr, map) { + var clone = arr.slice(0), + newIndex = 0; + for (var i=0; i<map.length; i++) { + newIndex = map[i]; + clone[newIndex] = arr[i]; + } + return clone; + }; + + // ==================================================== // + // Begin execution! // + // ==================================================== // + + // Do sorting when THs are clicked + $table.on("click", "th", function() { + var trs = $table.children("tbody").children("tr"); + var $this = $(this); + var th_index = 0; + var dir = $.fn.stupidtable.dir; + + $table.find("th").slice(0, $this.index()).each(function() { + var cols = $(this).attr("colspan") || 1; + th_index += parseInt(cols,10); + }); + + // Determine (and/or reverse) sorting direction, default `asc` + var sort_dir = $this.data("sort-default") || dir.ASC; + if ($this.data("sort-dir")) + sort_dir = $this.data("sort-dir") === dir.ASC ? dir.DESC : dir.ASC; + + // Choose appropriate sorting function. + var type = $this.data("sort") || null; + + // Prevent sorting if no type defined + if (type === null) { + return; + } + + // Trigger `beforetablesort` event that calling scripts can hook into; + // pass parameters for sorted column index and sorting direction + $table.trigger("beforetablesort", {column: th_index, direction: sort_dir}); + // More reliable method of forcing a redraw + $table.css("display"); + + // Run sorting asynchronously on a timout to force browser redraw after + // `beforetablesort` callback. Also avoids locking up the browser too much. + setTimeout(function() { + // Gather the elements for this column + var column = []; + var sortMethod = sortFns[type]; + + // Push either the value of the `data-order-by` attribute if specified + // or just the text() value in this column to column[] for comparison. + trs.each(function(index,tr) { + var $e = $(tr).children().eq(th_index); + var sort_val = $e.data("sort-value"); + var order_by = typeof(sort_val) !== "undefined" ? sort_val : $e.text(); + column.push(order_by); + }); + + // Create the sort map. This column having a sort-dir implies it was + // the last column sorted. As long as no data-sort-desc is specified, + // we're free to just reverse the column. + var theMap; + if (sort_dir == dir.ASC) + theMap = sort_map(column, sortMethod); + else + theMap = sort_map(column, function(a, b) { return -sortMethod(a, b); }); + + // Reset siblings + $table.find("th").data("sort-dir", null).removeClass("sorting-desc sorting-asc"); + $this.data("sort-dir", sort_dir).addClass("sorting-"+sort_dir); + + var sortedTRs = $(apply_sort_map(trs, theMap)); + $table.children("tbody").remove(); + $table.append("<tbody />").append(sortedTRs); + + // Trigger `aftertablesort` event. Similar to `beforetablesort` + $table.trigger("aftertablesort", {column: th_index, direction: sort_dir}); + // More reliable method of forcing a redraw + $table.css("display"); + }, 10); + }); + }); + }; + + // Enum containing sorting directions + $.fn.stupidtable.dir = {ASC: "asc", DESC: "desc"}; + + $.fn.stupidtable.default_sort_fns = { + "int": function(a, b) { + return parseInt(a, 10) - parseInt(b, 10); + }, + "float": function(a, b) { + return parseFloat(a) - parseFloat(b); + }, + "string": function(a, b) { + if (a < b) return -1; + if (a > b) return +1; + return 0; + }, + "string-ins": function(a, b) { + a = a.toLowerCase(); + b = b.toLowerCase(); + if (a < b) return -1; + if (a > b) return +1; + return 0; + } + }; + + })(jQuery); + $(function(){ + var table = $("table").stupidtable(); + + table.on("beforetablesort", function (event, data) { + // data.column - the index of the column sorted after a click + // data.direction - the sorting direction (either asc or desc) + $("#msg").text("Sorting index " + data.column) + }); + table.on("aftertablesort", function (event, data) { + var th = $(this).find("th"); + th.find(".arrow").remove(); + var dir = $.fn.stupidtable.dir; + var arrow = data.direction === dir.ASC ? "↑" : "↓"; + th.eq(data.column).append('<span class="arrow">' + arrow +'</span>'); + }); + }); + </script> + <style type="text/css"> + label { + display: inline-block; + width: 5em; + } + table { + border-collapse: collapse; + } + th, td { + padding: 5px 10px; + border: 1px solid #999; + } + th { + background-color: #a7d3ff; + } + th[data-sort]{ + cursor:pointer; + } + a[data-lightbox]{ + cursor:zoom-in; + } + #msg { + color: green; + } + </style> + </head> + <body> + <h1>Dual coding analyse results</h1> + %s + </body> +</html> """ % (gene_table) + + html_file = open(html,"w") + html_file.write(html_str) + html_file.close() + html_index = open(subfolder+'/index.html','w') + html_index.write(html_str) + html_index.close() + + + except Exception, e : + stop_err('Error during html page creation : ' + str( e ) ) + + + +def __main__(): + + #Parse command line options + parser = optparse.OptionParser() + parser.add_option("-g", "--gff", dest="gff", type= "string", + help="GFF annotation file", metavar="FILE") + + parser.add_option("-b", "--bam", dest="bamfile", type= "string", + help="Bam Ribo-Seq alignments ", metavar="FILE") + + parser.add_option("-f", "--fasta", dest="fasta", type= "string", + help="Fasta file ", metavar="FILE") + + parser.add_option("-d", "--dirout", dest="dirout", type= "string", + help="write report in this html file and in associated directory", metavar="STR,STR") + + parser.add_option("-x", "--boxplot", dest="box", type= "string", + help="report boxplot in this file", metavar="STR") + + parser.add_option("-c", "--cutoff", dest="cutoff", type= "int", default = 60 , + help="Integer value for selecting all genes that have less than 60 % (default) of reads in coding frame ", metavar="INT") + + parser.add_option("-k", "--kmer", dest="kmer", type= "int",default = 28 , + help="Longer of your phasing reads (Default is 28)", metavar="INT") + + parser.add_option("-o", "--orf_length", dest="orf_length", type= "int", default = 300 , + help="Mean of ORF's length for segmentation (Default is 300pb, recommended for yeast)", metavar="INT") + + parser.add_option("-p", "--frame", dest="frame", type="int", default = 1 , + help="Script can compute in frame 1, 2 or 3", metavar="1|2|3") + + parser.add_option("-q", "--quiet", + action="store_false", dest="verbose", default=True, + help="don't print status messages to stdout") + + (options, args) = parser.parse_args() + sys.stdout.write("Begin frame analysis at %s\n" % time.asctime( time.localtime(time.time()))) + # Create temp dir + try: + tmp_dir = tempfile.mkdtemp() + (html_file, subfolder ) = options.dirout.split(",") + + ##testing indexed bam file + if os.path.isfile(options.bamfile+".bai") : + pass + else : + cmd = "samtools index %s " % (options.bamfile) + proc = subprocess.Popen( args=cmd, shell=True, stderr = subprocess.PIPE) + returncode = proc.wait() + + if not os.path.exists(subfolder): + try: + os.mkdir(subfolder) + except Exception, e : + stop_err('Error running make directory : ' + str(e)) + + ## check frame arg + if options.frame not in [1,2,3]: + stop_err( 'Please enter a good value for frame argument : must be 1, 2 or 3' ) + + ##RUN analysis + split_bam(options.bamfile,tmp_dir) + get_first_base (tmp_dir,options.kmer,options.frame) + ## identify GFF or GTF format from 9th column + with open (options.gff,"r") as gffile : + for line in gffile : + if '#' in line : + ## skip header + gffile.next() + elif 'gene_id' in line : + ## launch gtf reader : + GFF = ribo_functions.store_gtf(options.gff) + break + elif 'ID=' in line : + ## launch gff reader + GFF = ribo_functions.store_gff(options.gff) + break + else : + stop_err( 'Please check your annotation file is in correct format, GFF or GTF' ) + + ## check gff reading + if not GFF['order'] : + stop_err( 'Incorrect GFF file') + for gene in GFF['order']: + if not GFF[gene]['exon'] : + del GFF[gene] + GFF['order'].remove(gene) + + #### cPickles for Test #### + #if os.path.isfile("/home/rlegendre/Documents/FrameShift/OutOfFrameWithIntron/pyt_dict") : + # with open("/home/rlegendre/Documents/FrameShift/OutOfFrameWithIntron/pyt_dict",'rb') as fp: + # GFF = cPickle.load(fp) + #else : + # split_bam(options.bamfile,tmp_dir) + # get_first_base (tmp_dir) + # GFF = store_gff(options.gfffile) + # GFF = frame_analysis (tmp_dir,options.outfile,options.cutoff,GFF,subfolder) + # with open("/home/rlegendre/OutOfFrameWithIntron/pyt_dict",'wb') as fp: + # cPickle.dump(GFF,fp) + # + #### cPickles for Test #### + + GFF = frame_analysis (tmp_dir,subfolder,options.cutoff,GFF, options.box, options.orf_length) + plot_subprofile (GFF,subfolder,options.fasta) + write_html_page(html_file,subfolder,GFF) + + ##paste jquery script in result directory : + jq_src = os.path.join(os.path.dirname(__file__),'lightbox') + shutil.copytree(jq_src,os.path.join(subfolder,'lightbox')) + + + + sys.stdout.write("Finish frame analysis at %s\n" % time.asctime( time.localtime(time.time()))) + except Exception, e: + stop_err( 'Error running metagene analysis : ' + str( e ) ) + + if os.path.exists( tmp_dir ): + shutil.rmtree( tmp_dir ) + + + +if __name__=="__main__": + __main__() \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ribo_tools/metagene_frameshift_analysis.xml Wed Sep 27 04:59:10 2017 -0400 @@ -0,0 +1,77 @@ +<tool id="frameshift_analysis" name="Frame"> + <description>To analyse Ribo-seq alignments for the extraction of translational ambiguities</description> + <requirements> + <requirement type="package">samtools</requirement> + <requirement type="python-module">matplotlib</requirement> + <requirement type="python-module">numpy</requirement> + <requirement type="python-module">PIL</requirement> + <requirement type="python-module">Bio</requirement> + </requirements> + <command interpreter="python"> + metagene_frameshift_analysis.py --gff $reference --bam $mapping --cutoff $cutoff --kmer $kmer --fasta $fasta --dirout $output,$output.files_path --box $boxplot --orf_length $orf --frame $frame > $log + + </command> + + <inputs> + <param name="reference" type="data" label="Reference annotation file (GFF)" format="gff" /> + <param name="mapping" type="data" label="Bam file" format="bam" /> + <param name="fasta" type="data" label="Reference genome in Fasta format" format="fasta" /> + <param name="kmer" type="integer" label="Length of the best phasing footprints" value ="28" /> + <param name="frame" type="integer" label="Frame where footprints show best phasing. Must be 1, 2 or 3" value ="1" /> + <param name="cutoff" type="integer" label="Cutoff for frame proportion in coding phase (default = 60 %)" value ="60" /> + <param name="orf" type="integer" label="Approximate size of the segment (in bp)" value ="300" /> + </inputs> + + <outputs> + <data format="tabular" name="log" label="[RP]Stat File on ${on_string}"/> + <data format="html" name="output" label="[RP]Dual coding results on ${on_string}"/> + <data format="png" name="boxplot" label="[RP]Boxplot on ${on_string}"/> + + </outputs> + + <help> +Summary +------- +This tool uses Ribo-seq data (BAM file) to extract out-of-frame footprints in all genes from a reference annotation file (`GFF3`_). Subprofiles are plotted for each gene with dual coding events. + + +*- GFF3 file*: This file must have nine tabulated-delimited columns: Chromosome, source, feature, start, stop, score, strand, phasing, note. The gene ID is retrieved from the note field, using the "ID=" tag. + +*- Fasta file*: Reference fasta file. Care should be taken with the chromosome nomenclature used, which must be compatible with the GFF3 annotation file. + +*- BAM file*: This file should be sorted and may contain either multiple or unaligned footprints + +*- Kmer*: Length of the best-phased footprints. It can be calculated by running kmer_analysis + +*- Frame*: Frame for which the phasing of the footprints is best. It can be calculated by running kmer_analysis + +*- Cutoff*: An integer value for selecting all genes for which fewer than 60 % (default) of the footprints are in the coding frame. + +*- Orf*: Approximate size of the segment. + + +.. _GFF3: http://gmod.org/wiki/GFF3 + + +Output +------- +This tool generates 3 output files: + +*- html file*: for the detection and visualisation of translational ambiguities. + +*- Stat file*: this file provides statistics for the treated footprints and phasing. + +*- Boxplot*: Proportion of footprints in the three frames, for all genes. + + +Dependances +------------ + +.. class:: warningmark + +This tool depends on Python (>=2.7) and following packages : numpy 1.8.0, Biopython 1.58, matplotlib 1.3.1. Samtools is used for bam manipulation. + + + </help> +</tool> +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ribo_tools/metagene_readthrough.py Wed Sep 27 04:59:10 2017 -0400 @@ -0,0 +1,1056 @@ +#!/usr/bin/env python2.7.3 +#-*- coding: utf-8 -*- + +''' + Created on Dec. 2013 + @author: rachel legendre + @copyright: rachel.legendre@igmors.u-psud.fr + @license: GPL v3 +''' + +import os, sys, time, optparse, shutil, re, urllib, subprocess, tempfile +from urllib import unquote +from Bio import SeqIO +import csv +import pysam +import HTSeq +#from matplotlib import pyplot as pl +import matplotlib +from jinja2.nodes import Pos +matplotlib.use('Agg') +import matplotlib.pyplot as pl +from numpy import arange +from matplotlib import ticker as t +from PIL import Image +import ribo_functions +## suppress matplotlib warnings +import warnings + + + +def stop_err( msg ): + sys.stderr.write( "%s\n" % msg ) + sys.stderr.write( "Programme aborted at %s\n" % time.asctime(time.localtime(time.time()))) + sys.exit() + + +def compute_rpkm(length,count_gene,count_tot) : + + try : + rpkm = "{0:.4f}".format(count_gene*1000000.0/(count_tot*length)) + except ArithmeticError : + stop_err( 'Illegal division by zero during computing RPKM') + return float(rpkm) + + +def find_stop(seq) : + ''' + Find stop codon in a sequence and return position of first nucleotide in stop + ''' + stop_codon = ['TAA','TAG','TGA'] + for nt in range(0,len(seq)-3,3) : + codon = seq[nt:nt+3] + if codon in stop_codon : + return nt + + return -1 + + +def check_met(seq): + ''' + Boolean function for testing presence or absence of methionine in 5 codons following stop codon + ''' + met = 'ATG' + for pos in range(0,15,3) : + codon = seq[pos:pos+3] + if codon in met : + return True + + return False + + +''' + feature.iv is a GenomicInterval object : + A GenomicInterval object has the following slots, some of which + are calculated from the other: + + chrom: The name of a sequence (i.e., chromosome, contig, or + the like). + start: The start of the interval. Even on the reverse strand, + this is always the smaller of the two values 'start' and 'end'. + Note that all positions should be given as 0-based value! + end: The end of the interval. Following Python convention for + ranges, this in one more than the coordinate of the last base + that is considered part of the sequence. + strand: The strand, as a single character, '+' or '-'. '.' indicates + that the strand is irrelevant. (Alternatively, pass a Strand object.) + length: The length of the interval, i.e., end - start + start_d: The "directional start" position. This is the position of the + first base of the interval, taking the strand into account. Hence, + this is the same as 'start' except when strand == '-', in which + case it is end-1. + end_d: The "directional end": Usually, the same as 'end', but for + strand=='-1', it is start-1. + +''' +def check_overlapping(gff_reader,chrm,start,stop,strand, name): + + #### probleme avec les genes completement inclu... + + iv2 = HTSeq.GenomicInterval(chrm,start,stop,strand) + for feature in gff_reader: + if feature.type == "gene" : + if feature.iv.overlaps(iv2) and feature.attr.get('gene_name') != name : + ## if its a reverse gene, we replace start of extension by start of previous gene + if strand == '-' : + return (feature.iv.end+3,stop) + ## else we replace stop of extension by start of following gene + else : + return (start,feature.iv.start-3) + ## if no overlap are find, we return -1 + return (start,stop) + + +def pass_length(start,stop) : + + if (stop-start) > 25 : + return True + else : + return False + + +def check_homo_coverage(gene,GFF,start,stop, aln) : + + chrom = GFF[gene]['chrom'] + ## compute number of nucleotides in CDS with a coverage equal to zero + nt_cds_cov = 0 + nt_cds_num = 0 + for i in range(1,GFF[gene]['exon_number']+1) : + for z in range(GFF[gene]['exon'][i]['start'],GFF[gene]['exon'][i]['stop']): + nt_cds_num += 1 + if aln.count(chrom,z,z+1) == 0 : + nt_cds_cov += 1 + + ## compute percent of CDS no covering + if nt_cds_cov == 0 or nt_cds_num == 0: + percent = 0 + else: + percent = nt_cds_cov*100/nt_cds_num + + ## compute number of nucleotides with no coverage in extension + nt_no_cover = 0 + for pos in range(start,stop,1) : + if aln.count(chrom,pos,pos+1) == 0 : + nt_no_cover += 1 + #print gene, nt_cds_cov, percent, nt_no_cover + #percent10 = (stop-start)*50/100 + if (nt_no_cover*100)/(stop-start) > percent : + return False + else : + return True + +def plot_gene ( aln, gene, start_extension, stop_extension, dirout ) : + + + ### ignore matplotlib warnings for galaxy + warnings.filterwarnings('ignore') + try: + strand = gene['strand'] + len_gene = gene['stop']-gene['start'] + if strand is "-" : + len_ext = gene['stop']-start_extension + ## coverage in all gene + extension + start = start_extension-100 + stop = gene['stop']+100 + vector1 = [0]*(stop-start) + #for pileupcolumn in aln.pileup( gene['chrom'], start, stop): + # vector.append(pileupcolumn.n) + for read in aln.fetch(gene['chrom'], start, stop): + if read.is_reverse : + ## for get footprint in P-site (estimate) we take 3 nt in middle of read + #pos = (read.pos+13)-start + pos = (read.pos-start) + (read.rlen/2)-1 + for z in range(pos,(pos+3)) : + ## le fetch contient des reads qui chevauchent les 30 nt de la fin du gene mais dont le site P + ## se trouve avant notre vector, le z devient negatif et la couverture augmente en fin de vecteur (ce qui est faux) + if z > 0 : + try : + vector1[z] += 1 + except IndexError : + pass + vector1.reverse() + mean_read = float(sum(vector1))/float(len(vector1)) + cov = [(x/mean_read) for x in vector1] + idx_tot = arange(len(cov)) + ## coverage in extension + start_ext = start_extension-40 + stop_ext = stop_extension+30 + vector2 = [0]*(stop_ext-start_ext) + #for pileupcolumn in aln.pileup( gene['chrom'], start, stop): + # vector.append(pileupcolumn.n) + for read in aln.fetch(gene['chrom'], start_extension, stop_ext): + if read.is_reverse : + ## get footprint in P-site + #pos = (read.pos+13)-start_ext + pos = (read.pos-start_ext) + (read.rlen/2)-1 + for z in range(pos,(pos+3)) : + if z > 0 : + try : + vector2[z] += 1 + except IndexError : + pass + vector2.reverse() + #mean_read = float(sum(vector))/float(len(vector)) + cov_ext = [(x/mean_read) for x in vector2] + _max = max(cov_ext[30::]) + idx_ext = arange(len(cov_ext)) + + else : + len_ext = stop_extension-gene['start'] + start = gene['start']-100 + stop = stop_extension+100 + vector = [0]*(stop-start) + #for pileupcolumn in aln.pileup( gene['chrom'], start, stop): + #vector.append(pileupcolumn.n) + for read in aln.fetch(gene['chrom'], start, stop): + if not read.is_reverse : + ## get footprint in P-site + #pos = (read.pos+12)-start + pos = (read.pos-start) + (read.rlen/2)-1 + for z in range(pos,(pos+3)) : + if z > 0 : + try : + vector[z] += 1 + except IndexError : + pass + mean_read = float(sum(vector))/float(len(vector)) + cov = [(x/mean_read) for x in vector] + idx_tot = arange(len(cov)) + + ## coverage in extension + start_ext = gene['stop']-30 + stop_ext = stop_extension+40 + vector = [0]*(stop-start_ext) + for read in aln.fetch(gene['chrom'], start_ext, stop_extension): + if not read.is_reverse : + ## get footprint in P-site + pos = (read.pos-start_ext) + (read.rlen/2)-1 + for z in range(pos,(pos+3)) : + if z > 0 : + try : + vector[z] += 1 + except IndexError : + pass + + cov_ext = [(x/mean_read) for x in vector] + idx_ext = arange(len(cov_ext)) + _max = max(cov_ext[30::]) + + #### PLOT FIGURE #### + + font = {'family' : 'serif','color': 'grey','weight' : 'normal','size' : 16 } + + + fig = pl.figure(num=1) + ## create a big subplot for common y axis on two last subplot + ax = fig.add_subplot(2,1,2) + ## hide all spines + ax.spines['top'].set_visible(False) + ax.spines['bottom'].set_visible(False) + ax.spines['left'].set_visible(False) + ax.spines['right'].set_visible(False) + ax.tick_params(labelcolor='w', top='off', bottom='off', left='off', right='off') + + ## plot gene structure + ax1 = fig.add_subplot(3,1,1) + ax1.set_title(gene['name']) + ## hide all spines + ax1.spines['right'].set_color('none') + ax1.spines['top'].set_color('none') + ax1.spines['left'].set_color('none') + ax1.spines['bottom'].set_color('none') + ax1.set_ylim(0,5) + #set xlim as second plot with 100nt switch + ax1.set_xlim(-100,len(idx_tot)-100) + ## hide ticks + pl.yticks([]) + pl.xticks([]) + ## if it's a "simple" gene + if gene['exon_number'] == 1 : + exon = arange(len_gene) + x = [3]*len_gene + ax1.plot(exon,x,color='#9B9E9C') + ax1.fill_between(exon,2,x,color='#9B9E9C') + ## if it's a gene with introns + else : + ## plot a line representing intron + intron = arange(len_gene) + y = [2.5]*len_gene + ax1.plot(intron,y,color='#9B9E9C') + + ## plotting each intron + start = gene['start'] + if strand == '+' : + for exon in range(1,gene['exon_number']+1,1) : + len_exon = gene['exon'][exon]['stop']-gene['exon'][exon]['start'] + idx = gene['exon'][exon]['start'] - start + exo = arange(idx,idx+len_exon) + x = [3]*len_exon + ax1.plot(exo,x,color='#9B9E9C') + ax1.fill_between(exo,2,x,color='#9B9E9C') + else : + ## if it's a reverse gene we must reverse exon's position + start = gene['start'] + tabF = [2.5]*len_gene + tabR = [2.5]*len_gene + for exon in range(1,gene['exon_number']+1,1) : + len_exon = gene['exon'][exon]['stop']-gene['exon'][exon]['start'] + idx = gene['exon'][exon]['start'] - start + exo = arange(idx,idx+len_exon) + for z in exo : + tabF[z] = 3 + tabR[z] = 2 + tabF.reverse() + tabR.reverse() + #pl.ylim(0,5) + ax1.plot(tabF,color='#9B9E9C') + ax1.plot(tabR,color='#9B9E9C') + x = arange(len(tabR)) + ax1.fill_between(x,tabF,tabR,color='#9B9E9C') + + ## insert arrows (as genome browser representation) + yloc = 2.5 + narrows = 20 + exonwidth = .8 + spread = .4 * len_gene / narrows + for i in range(narrows): + loc = (float(i) * len_gene / narrows)+ (len(idx_tot)/100)*2 + if strand == '+' : + x = [loc - spread, loc, loc - spread] + else: + x = [loc - spread, loc, loc - spread] + y = [yloc - exonwidth / 5, yloc, yloc + exonwidth / 5] + ax1.plot(x, y, lw=1.4, color='#676B69') + + + # plot coverage in all gene + extension + ax2 = fig.add_subplot(3,1,2) + ## fixe limit to length of coverage for x axis + ax2.set_xlim(0,len(idx_tot)) + ## fixe 4 ticks and associated labels for y axis + ax2.set_yticklabels(arange(0,int(max(cov)+20),int((max(cov)+20)/4))) + ax2.set_yticks(arange(0,int(max(cov)+20),int((max(cov)+20)/4))) + ### add start and stop of gene in axe in place of number + ax2.set_xticks([100,(100+len_gene),(100+len_ext)]) + ax2.set_xticklabels(["{:,}".format(gene['start']),"{:,}".format(gene['stop']),""]) + ## hide spines and any axis + ax2.spines['right'].set_visible(False) + ax2.spines['top'].set_visible(False) + ax2.yaxis.set_ticks_position('left') + ax2.xaxis.set_ticks_position('bottom') + ## plot and fill + ax2.plot(idx_tot, cov, color="#CC0011") + ax2.fill_between(idx_tot, 0,cov,color="#CC0011") + ax2.set_title("Genomic coordinates (%s,%s)"% (gene['strand'],gene['chrom']) ,fontdict={'fontsize':'small'}) + + # plot zoom coverage in extension + ax3 = fig.add_subplot(3,1,3) + ## fixe limit to length of coverage for x axis + ax3.set_ylim(0,int(_max)) + # we hide spines + ax3.spines['right'].set_visible(False) + ax3.spines['top'].set_visible(False) + ax3.yaxis.set_ticks_position('left') + ax3.xaxis.set_ticks_position('bottom') + ## add stop and in-frame stop in x axis + pl.xticks([30,( stop_extension-start_extension)+30],["stop codon","next in-frame stop"]) + ## fixe good position for labels + (ax3.get_xticklabels()[0]).set_horizontalalignment('center') + (ax3.get_xticklabels()[1]).set_horizontalalignment('left') + #if max coverage is lower than 2, we have a illegal division by zero + if _max > 2 : + ax3.set_yticklabels(arange(0,int(_max+1),int((_max+1)/3))) + ax3.set_yticks(arange(0,int(_max+1),int((_max+1)/3))) + else : + ## + ax3.set_ylim(0,_max) + ax3.ticklabel_format(style='sci', scilimits=(-2,2), axis='y',useOffset=False) + + ax3.plot(idx_ext, cov_ext, color="#CC0011") + ax3.fill_between(idx_ext, 0,cov_ext,color="#CC0011") + + + ## get scale of subplot 3 + #ax3.text(ax3.get_xlim()[1]-50,ax3.get_ylim()[1]-1, r'50 nt', fontdict=font) + #pl.arrow( ax3.get_xlim()[1]-50, ax3.get_ylim()[1]-2, ax3.get_xlim()[1]-50, 0, fc="grey", ec="grey",lw=2) + + ## set common y label + ax.set_ylabel('Normalized footprint counts',labelpad=20) + + ## draw and save plot + pl.draw() + #pl.show() + pl.savefig(dirout+".png",format='png', dpi=300) + pl.clf() + + + ## Make thumbnail for html page + infile = dirout+'.png' + size = 128, 128 + im = Image.open(infile) + im.thumbnail(size, Image.ANTIALIAS) + im.save(dirout+"_thumbnail.png", "PNG") + warnings.resetwarnings() + + except Exception, e: + stop_err( 'Error during gene plotting : ' + str( e ) ) + + +def __percent__(prop): + + if sum(prop) != 0 : + perc = [0,0,0] + if prop[0] != 0 : + perc[0] = int("{0:.0f}".format((prop[0]*100.0)/sum(prop))) + if prop[1] != 0 : + perc[1] = int("{0:.0f}".format((prop[1]*100.0)/sum(prop))) + if prop[2] != 0 : + perc[2] = int("{0:.0f}".format((prop[2]*100.0)/sum(prop))) + return perc + else : + return prop + +def compute_phasing(chrom, start, stop, aln, kmer, strand): + + phasing = [0,0,0] + for track in aln.fetch(chrom, start, stop): + if strand == '-': + if track.is_reverse : + if len(track.seq) == kmer : + pos = stop - (track.pos + track.rlen - 12) + if pos > 0: + phasing[pos%3] += 1 + else : + if not track.is_reverse : + if len(track.seq) == kmer : + pos = (track.pos + 12) - start + if pos > 0: + phasing[pos%3] += 1 + + #return __percent__(phasing) + return phasing + + +def compute_analysis(bam, GFF, fasta, gff, dirout, extend, kmer) : + + try: + #background_file = dirout+"/background_sequence.fasta" + #file_back = open(background_file, 'w') + #file_context = open(dirout+"/stop_context.fasta", 'w') + #file_extension = open(dirout+"/extensions.fasta", 'w') + ## Opening Bam file with pysam librarie + pysam.index(bam) + aln = pysam.Samfile(bam, "rb",header=True, check_header = True) + ## Opening fasta file in a dict with BioPython + SeqDict = SeqIO.to_dict(SeqIO.parse(open(fasta,"r"),"fasta")) + + ## count total read in bam file + cmd = "samtools view -c %s " % (bam) + proc = subprocess.Popen( args=cmd, shell=True, stdout = subprocess.PIPE) + count_tot = int(proc.stdout.read().rstrip()) + returncode = proc.wait() + + ## opening a GFF reader for check overlapping + gff_reader = HTSeq.GFF_Reader(gff) + + with open(dirout+"/readthrough_result.csv","w") as out : + myheader = ['Gene','Name', 'FAIL', 'Stop context','chrom','start extension','stop extension','length extension','RPKM CDS', 'RPKM extension','ratio','Annotation','sequence'] + writer = csv.writer(out,delimiter=',') + writer.writerow(myheader) + for gene in GFF['order'] : + #print GFF[gene] + ## maybe no start position in GTF file so we must to check and replace + exon_number = GFF[gene]['exon_number'] + try : GFF[gene]['start'] + except : + if GFF[gene]['strand'] == '+' : + GFF[gene]['start'] = GFF[gene]['exon'][1]['start'] + else : + GFF[gene]['start'] = GFF[gene]['exon'][exon_number]['stop'] + ## also for stop coordinates + try : GFF[gene]['stop'] + except : + if GFF[gene]['strand'] == '+' : + GFF[gene]['stop'] = GFF[gene]['exon'][exon_number]['stop'] + + else : + GFF[gene]['stop'] = GFF[gene]['exon'][1]['start'] + + + indic = "" + # compute rpkm of CDS : + len_cds = GFF[gene]['stop']-GFF[gene]['start'] + count_cds = 0 + rpkm_cds = 0 + count_cds = 0 + try : + ### count method of pysam doesn't strand information + if GFF[gene]['strand'] == '+' : + for track in aln.fetch(GFF[gene]['chrom'],GFF[gene]['start']+12,GFF[gene]['stop']-15) : + if not track.is_reverse : + count_cds += 1 + else : + for track in aln.fetch(GFF[gene]['chrom'],GFF[gene]['start']+15,GFF[gene]['stop']-12) : + if track.is_reverse : + count_cds += 1 + rpkm_cds = compute_rpkm(len_cds,count_cds,count_tot) + except ValueError: + ## genere warning about gtf coordinates + #warnings.warn("Please check coordinates in GFF : maybe stop or start codon are missing" ) + pass + ## Only if gene is translate : + if rpkm_cds > 0 and count_cds > 128: + ## search footprint in UTR3 + count = 0 + try : + if GFF[gene]['strand'] == '+' : + contexte_stop = str(SeqDict[GFF[gene]['chrom']].seq[GFF[gene]['stop']-6:GFF[gene]['stop']+6]) + #print gene, contexte_stop + start_extension = GFF[gene]['stop'] + stop_extension = GFF[gene]['stop']+90 + for track in aln.fetch(GFF[gene]['chrom'],start_extension+15,stop_extension) : + if not track.is_reverse : + count += 1 + + ## get sequence of extension + seq = str(SeqDict[GFF[gene]['chrom']].seq[start_extension:stop_extension]) + + else : + contexte_stop = str(SeqDict[GFF[gene]['chrom']].seq[GFF[gene]['start']-7:GFF[gene]['start']+5].reverse_complement()) + #print gene, contexte_stop + start_extension = GFF[gene]['start']-90 + stop_extension = GFF[gene]['start'] + for track in aln.fetch(GFF[gene]['chrom'],start_extension,stop_extension-15) : + if track.is_reverse : + count += 1 + ## get sequence of extension + seq = str(SeqDict[GFF[gene]['chrom']].seq[start_extension:stop_extension-1].reverse_complement()) + + + ## if we have coverage after cds stop codon + if count > 10 : + res = find_stop(seq) + if res == -1 : + ''' + Write results with no stop but RPF in extension + ''' + ## check if next in-frame codon is far than 90 nt extension : + if GFF[gene]['strand'] == '+' : + pos = check_overlapping(gff_reader,GFF[gene]['chrom'],GFF[gene]['stop']+1,GFF[gene]['stop']+extend,'+',GFF[gene]['name']) + start_extension = pos[0]-1 + stop_extension = pos[1] + #start_extension = GFF[gene]['stop'] + #stop_extension = GFF[gene]['stop']+extend + seq = str(SeqDict[GFF[gene]['chrom']].seq[start_extension:stop_extension]) + + #print gene,count,pos,'\n',seq + + if (seq): + res = find_stop(seq) + if res == -1 : + mylist = [gene,GFF[gene]['name'],'no stop',contexte_stop, GFF[gene]['chrom'], start_extension, stop_extension,stop_extension-start_extension,rpkm_cds,'-','-',seq,GFF[gene]['note']] + writer.writerow(mylist) + else : + indic = 'ok' + #print res + #stop_extension = start_extension + res +3 + else : + pos = check_overlapping(gff_reader,GFF[gene]['chrom'],GFF[gene]['start']-extend,GFF[gene]['start']-1,'-',GFF[gene]['name']) + start_extension = pos[0] + stop_extension = pos[1]+1 + #start_extension = GFF[gene]['start']-extend + #stop_extension = GFF[gene]['start'] + seq = str(SeqDict[GFF[gene]['chrom']].seq[start_extension:stop_extension-1].reverse_complement()) + + #print gene,count,pos,'\n',seq + + if (seq): + res = find_stop(seq) + if res == -1 : + mylist = [gene,GFF[gene]['name'],'no stop',contexte_stop, GFF[gene]['chrom'], start_extension, stop_extension,stop_extension-start_extension,rpkm_cds,'-','-',seq,GFF[gene]['note']] + writer.writerow(mylist) + else : + indic = 'ok' + #print res + #start_extension = stop_extension - res -3 + else : + indic = 'ok' + + + if indic == 'ok' : + ## We save new coordinates + if GFF[gene]['strand'] == '+' : + stop_extension = start_extension + res +3 + #print gene, count + #print gene,start_extension,stop_extension + seq = str(SeqDict[GFF[gene]['chrom']].seq[start_extension:stop_extension]) + #print seq + count_stop = aln.count(GFF[gene]['chrom'],stop_extension-2,stop_extension+2) + if pass_length(start_extension,stop_extension) : + count_ext = aln.count(GFF[gene]['chrom'],start_extension+9,stop_extension-15) + if stop_extension > GFF[gene]['stop']+9 : + stop_ok = 1 + else : + stop_ok = 0 + else : + start_extension = stop_extension - res - 3 + #print gene, count + #print gene,start_extension,stop_extension + seq = str(SeqDict[GFF[gene]['chrom']].seq[start_extension-1:stop_extension-1].reverse_complement()) + #print seq + count_stop = aln.count(GFF[gene]['chrom'],start_extension-2,start_extension+2) + if pass_length(start_extension,stop_extension) : + count_ext = aln.count(GFF[gene]['chrom'],start_extension+15,stop_extension-9) + if start_extension < GFF[gene]['start']-9 : + stop_ok = 1 + else : + stop_ok = 0 + ## if we are no methionine in 5 codons following stop of CDS + if (not check_met(seq) ): + ## if we have footprint in stop codon extension and stop extension is further than cds_stop+9 + if count_stop > 2 and stop_ok == 1 : + homo_cov = check_homo_coverage(gene,GFF,start_extension,stop_extension,aln) + # phasing = compute_phasing(GFF[gene]['chrom'],start_extension,stop_extension, aln, kmer,GFF[gene]['strand']) + # print gene, phasing +# count_after_stop = 0 +# try : +# ### count method of pysam doesn't strand information +# if GFF[gene]['strand'] == '+' : +# for track in aln.fetch(GFF[gene]['chrom'],stop_extension+15,stop_extension+40) : +# if not track.is_reverse : +# count_after_stop += 1 +# else : +# for track in aln.fetch(GFF[gene]['chrom'],start_extension-40,start_extension-15) : +# if track.is_reverse : +# count_after_stop += 1 +# except ValueError: +# ## genere warning about gtf coordinates +# warnings.warn("Please check coordinates in GFF : maybe stop or start codon are missing" ) +# pass +# print gene+"\t"+str(count_stop)+"\t"+str(count_after_stop) +# if (count_stop*5)/100 > count_after_stop : +# print "moins de 5%...\n" + if (homo_cov) : + ''' + write result witch corresponding to readthrough + ''' + ##if length of extension upper than 25 we can compute rpkm + if (not pass_length(start_extension,stop_extension)) : + len_ext = stop_extension-start_extension + rpkm_ext = 'nan' + ratio = 'nan' + else : + len_ext = stop_extension-start_extension + rpkm_ext = compute_rpkm(len_ext,count_ext,count_tot) + ## compute ratio between extension coverage and cds coverage (rpkm) + ratio = rpkm_ext/rpkm_cds + #print gene,ratio + #print start_extension,stop_extension + mylist = [gene,GFF[gene]['name'],'-',contexte_stop,GFF[gene]['chrom'], start_extension, stop_extension,stop_extension-start_extension,rpkm_cds,rpkm_ext,ratio,seq,GFF[gene]['note']] + writer.writerow(mylist) + #file_context.write('>'+gene+'\n'+contexte_stop+'\n') + #file_extension.write('>'+gene+'\n'+seq+'\n') + else : + ''' + write result witch corresponding to readthrough but with no homogeneous coverage + ''' + if (not pass_length(start_extension,stop_extension)) : + len_ext = stop_extension-start_extension + rpkm_ext = 'nan' + ratio = 'nan' + else : + len_ext = stop_extension-start_extension + rpkm_ext = compute_rpkm(len_ext,count_ext,count_tot) + ## compute ratio between extension coverage and cds coverage (rpkm) + ratio = rpkm_ext/rpkm_cds + mylist = [gene,GFF[gene]['name'],'hetero cov',contexte_stop, GFF[gene]['chrom'], start_extension, stop_extension,stop_extension-start_extension,rpkm_cds,rpkm_ext,ratio,seq,GFF[gene]['note']] + writer.writerow(mylist) + #file_context.write('>'+gene+'\n'+contexte_stop+'\n') + #file_extension.write('>'+gene+'\n'+seq+'\n') + #print ">"+gene+"\n"+contexte_stop + + ## plot gene : + plot_gene(aln, GFF[gene], start_extension, stop_extension, dirout+"/"+gene) + + + + else : + ''' + write result with no footprint in stop codon of extension + ''' + mylist = [gene,GFF[gene]['name'],'no RPF in stop',contexte_stop, GFF[gene]['chrom'], start_extension, stop_extension,stop_extension-start_extension,rpkm_cds,'-','-',seq,GFF[gene]['note']] + writer.writerow(mylist) + #file_context.write('>'+gene+'\n'+contexte_stop+'\n') + #file_extension.write('>'+gene+'\n'+seq+'\n') + #print ">"+gene+"\n"+contexte_stop + else : + ''' + write result with RPF maybe result of reinitiation on a start codon + ''' + if pass_length(start_extension,stop_extension) : + mylist = [gene,GFF[gene]['name'],'Met after stop', contexte_stop, GFF[gene]['chrom'], start_extension, stop_extension,stop_extension-start_extension,rpkm_cds,'-','-',seq,GFF[gene]['note']] + writer.writerow(mylist) + #file_context.write('>'+gene+'\n'+contexte_stop+'\n') + #file_extension.write('>'+gene+'\n'+seq+'\n') + #print ">"+gene+"\n"+contexte_stop +# else : +# ## if its not a interesting case, we get stop context of genes without readthrough +# if GFF[gene]['strand'] == '+' : +# contexte_stop = str(SeqDict[GFF[gene]['chrom']].seq[GFF[gene]['stop']-6:GFF[gene]['stop']+6]) +# file_back.write(contexte_stop+'\n') +# else : +# contexte_stop = str(SeqDict[GFF[gene]['chrom']].seq[GFF[gene]['start']-7:GFF[gene]['start']+5].reverse_complement()) +# file_back.write(contexte_stop+'\n') +# + ## excluded UT with incorrect positions + except ValueError: + pass + + + #file_context.close() + #file_back.close() + #file_extension.close() + except Exception,e: + stop_err( 'Error during computing analysis : ' + str( e ) ) + + + +def write_html_page(html,subfolder) : + + + try : + + gene_table = '' + gene_table += '<table>' + gene_table += '<thead><tr><th data-sort="string">Gene</th><th>Plot</th><th data-sort="string">Name</th><th>Stop context</th><th>Coordinates</th><th>RPKM CDS</th><th>RPKM extension</th><th data-sort="float">ratio</th><th>Extension</th></tr></thead><tbody>' + + with open(os.path.join(subfolder,'readthrough_result.csv'), 'rb') as csvfile: + spamreader = csv.reader(csvfile, delimiter=',') + ## skip the header + next(spamreader, None) + ##test if file is empty or not + if next(spamreader, None): + for row in spamreader: + if row[2] == '-' : + gene_table += '<tr><td>%s</td><td><a href="%s.png" data-lightbox="%s"><img src="%s_thumbnail.png" /></a></td><td><a title="%s">%s</a></td><td>%s</td><td>%s:%s-%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>' %(row[0], row[0], row[0], row[0], row[11], row[1], row[3], row[4], row[5], row[6], row[8], row[9], row[10], row[11]) + + gene_table += '</tbody></table>' + else : + gene_table = 'Sorry, there are no stop codon readthrough genes in your data\n' + + + html_str = """ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> + +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <link rel="stylesheet" href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" /> + <script src="http://code.jquery.com/jquery-1.10.2.min.js"></script> + <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> + <script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script> + <script src="lightbox/js/jquery-1.10.2.min.js"></script> + <script src="lightbox/js/lightbox-2.6.min.js"></script> + <link href="lightbox/css/lightbox.css" rel="stylesheet" /> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> + <title>Dual coding result file</title> + <link rel="stylesheet" href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" /> + <script> + (function(d){d.fn.stupidtable=function(b){return this.each(function(){var a=d(this);b=b||{};b=d.extend({},d.fn.stupidtable.default_sort_fns,b);var n=function(a,b){for(var f=[],c=0,e=a.slice(0).sort(b),h=0;h<a.length;h++){for(c=d.inArray(a[h],e);-1!=d.inArray(c,f);)c++;f.push(c)}return f},q=function(a,b){for(var d=a.slice(0),c=0,e=0;e<b.length;e++)c=b[e],d[c]=a[e];return d};a.on("click","th",function(){var m=a.children("tbody").children("tr"),g=d(this),f=0,c=d.fn.stupidtable.dir;a.find("th").slice(0, g.index()).each(function(){var a=d(this).attr("colspan")||1;f+=parseInt(a,10)});var e=g.data("sort-default")||c.ASC;g.data("sort-dir")&&(e=g.data("sort-dir")===c.ASC?c.DESC:c.ASC);var h=g.data("sort")||null;null!==h&&(a.trigger("beforetablesort",{column:f,direction:e}),a.css("display"),setTimeout(function(){var l=[],p=b[h];m.each(function(a,b){var c=d(b).children().eq(f),e=c.data("sort-value"),c="undefined"!==typeof e?e:c.text();l.push(c)});var k;k=e==c.ASC?n(l,p):n(l,function(a,b){return-p(a,b)}); a.find("th").data("sort-dir",null).removeClass("sorting-desc sorting-asc");g.data("sort-dir",e).addClass("sorting-"+e);k=d(q(m,k));a.children("tbody").remove();a.append("<tbody />").append(k);a.trigger("aftertablesort",{column:f,direction:e});a.css("display")},10))})})};d.fn.stupidtable.dir={ASC:"asc",DESC:"desc"};d.fn.stupidtable.default_sort_fns={"int":function(b,a){return parseInt(b,10)-parseInt(a,10)},"float":function(b,a){return parseFloat(b)-parseFloat(a)},string:function(b,a){return b<a?-1: b>a?1:0},"string-ins":function(b,a){b=b.toLowerCase();a=a.toLowerCase();return b<a?-1:b>a?1:0}}})(jQuery); + (function($) { + + $.fn.stupidtable = function(sortFns) { + return this.each(function() { + var $table = $(this); + sortFns = sortFns || {}; + + // ==================================================== // + // Utility functions // + // ==================================================== // + + // Merge sort functions with some default sort functions. + sortFns = $.extend({}, $.fn.stupidtable.default_sort_fns, sortFns); + + // Return the resulting indexes of a sort so we can apply + // this result elsewhere. This returns an array of index numbers. + // return[0] = x means "arr's 0th element is now at x" + var sort_map = function(arr, sort_function) { + var map = []; + var index = 0; + var sorted = arr.slice(0).sort(sort_function); + for (var i=0; i<arr.length; i++) { + index = $.inArray(arr[i], sorted); + + // If this index is already in the map, look for the next index. + // This handles the case of duplicate entries. + while ($.inArray(index, map) != -1) { + index++; + } + map.push(index); + } + + return map; + }; + + // Apply a sort map to the array. + var apply_sort_map = function(arr, map) { + var clone = arr.slice(0), + newIndex = 0; + for (var i=0; i<map.length; i++) { + newIndex = map[i]; + clone[newIndex] = arr[i]; + } + return clone; + }; + + // ==================================================== // + // Begin execution! // + // ==================================================== // + + // Do sorting when THs are clicked + $table.on("click", "th", function() { + var trs = $table.children("tbody").children("tr"); + var $this = $(this); + var th_index = 0; + var dir = $.fn.stupidtable.dir; + + $table.find("th").slice(0, $this.index()).each(function() { + var cols = $(this).attr("colspan") || 1; + th_index += parseInt(cols,10); + }); + + // Determine (and/or reverse) sorting direction, default `asc` + var sort_dir = $this.data("sort-default") || dir.ASC; + if ($this.data("sort-dir")) + sort_dir = $this.data("sort-dir") === dir.ASC ? dir.DESC : dir.ASC; + + // Choose appropriate sorting function. + var type = $this.data("sort") || null; + + // Prevent sorting if no type defined + if (type === null) { + return; + } + + // Trigger `beforetablesort` event that calling scripts can hook into; + // pass parameters for sorted column index and sorting direction + $table.trigger("beforetablesort", {column: th_index, direction: sort_dir}); + // More reliable method of forcing a redraw + $table.css("display"); + + // Run sorting asynchronously on a timout to force browser redraw after + // `beforetablesort` callback. Also avoids locking up the browser too much. + setTimeout(function() { + // Gather the elements for this column + var column = []; + var sortMethod = sortFns[type]; + + // Push either the value of the `data-order-by` attribute if specified + // or just the text() value in this column to column[] for comparison. + trs.each(function(index,tr) { + var $e = $(tr).children().eq(th_index); + var sort_val = $e.data("sort-value"); + var order_by = typeof(sort_val) !== "undefined" ? sort_val : $e.text(); + column.push(order_by); + }); + + // Create the sort map. This column having a sort-dir implies it was + // the last column sorted. As long as no data-sort-desc is specified, + // we're free to just reverse the column. + var theMap; + if (sort_dir == dir.ASC) + theMap = sort_map(column, sortMethod); + else + theMap = sort_map(column, function(a, b) { return -sortMethod(a, b); }); + + // Reset siblings + $table.find("th").data("sort-dir", null).removeClass("sorting-desc sorting-asc"); + $this.data("sort-dir", sort_dir).addClass("sorting-"+sort_dir); + + var sortedTRs = $(apply_sort_map(trs, theMap)); + $table.children("tbody").remove(); + $table.append("<tbody />").append(sortedTRs); + + // Trigger `aftertablesort` event. Similar to `beforetablesort` + $table.trigger("aftertablesort", {column: th_index, direction: sort_dir}); + // More reliable method of forcing a redraw + $table.css("display"); + }, 10); + }); + }); + }; + + // Enum containing sorting directions + $.fn.stupidtable.dir = {ASC: "asc", DESC: "desc"}; + + $.fn.stupidtable.default_sort_fns = { + "int": function(a, b) { + return parseInt(a, 10) - parseInt(b, 10); + }, + "float": function(a, b) { + return parseFloat(a) - parseFloat(b); + }, + "string": function(a, b) { + if (a < b) return -1; + if (a > b) return +1; + return 0; + }, + "string-ins": function(a, b) { + a = a.toLowerCase(); + b = b.toLowerCase(); + if (a < b) return -1; + if (a > b) return +1; + return 0; + } + }; + + })(jQuery); + $(function(){ + var table = $("table").stupidtable(); + + table.on("beforetablesort", function (event, data) { + // data.column - the index of the column sorted after a click + // data.direction - the sorting direction (either asc or desc) + $("#msg").text("Sorting index " + data.column) + }); + table.on("aftertablesort", function (event, data) { + var th = $(this).find("th"); + th.find(".arrow").remove(); + var dir = $.fn.stupidtable.dir; + var arrow = data.direction === dir.ASC ? "↑" : "↓"; + th.eq(data.column).append('<span class="arrow">' + arrow +'</span>'); + }); + }); + </script> + <style type="text/css"> + label { + display: inline-block; + width: 5em; + } + table { + border-collapse: collapse; + } + th, td { + padding: 5px 10px; + border: 1px solid #999; + } + th { + background-color: #a7d3ff; + } + th[data-sort]{ + cursor:pointer; + } + a[data-lightbox]{ + cursor:zoom-in; + } + #msg { + color: green; + } + </style> + </head> + <body> + <h1>Readthrough analyse results</h1> + %s + </body> +</html> """ % (gene_table) + + html_file = open(html,"w") + html_file.write(html_str) + html_file.close() + + + except Exception, e : + stop_err('Error during html page creation : ' + str( e ) ) + + +def __main__(): + + #Parse command line options + parser = optparse.OptionParser() + parser.add_option("-g", "--gff", dest="gff", type= "string", + help="GFF annotation file", metavar="FILE") + + parser.add_option("-f", "--fasta", dest="fasta", type= "string", + help="Fasta file ", metavar="FILE") + + parser.add_option("-b", "--bam", dest="bamfile", type= "string", + help="Bam Ribo-Seq alignments ", metavar="FILE") + + parser.add_option("-d", "--dirout", dest="dirout", type= "string", + help="write report in this html file and in associated directory", metavar="STR,STR") + + parser.add_option("-k", "--kmer", dest="kmer", type= "int",default = 28 , + help="Longer of your phasing reads (Default is 28)", metavar="INT") + + parser.add_option("-e", "--extend", dest="extend", type= "int",default = 300 , + help="Lenght of extension after stop in number of base pairs (depends on your organisme)", metavar="INT") + + parser.add_option("-q", "--quiet", + action="store_false", dest="verbose", default=True, + help="don't print status messages to stdout") + + (options, args) = parser.parse_args() + sys.stdout.write("Begin readthrough analysis at %s\n" % time.asctime( time.localtime(time.time()))) + + try: + (html_file, subfolder ) = options.dirout.split(",") + if not os.path.exists(subfolder): + try: + os.mkdir(subfolder) + except Exception, e : + stop_err('Error running make directory : ' + str(e)) + ## identify GFF or GTF format from 9th column + with open (options.gff,"r") as gffile : + for line in gffile : + if '#' in line : + ## skip header + gffile.next() + elif 'gene_id' in line : + ## launch gtf reader : + GFF = ribo_functions.store_gtf(options.gff) + break + elif 'ID=' in line : + ## launch gff reader + GFF = ribo_functions.store_gff(options.gff) + break + else : + stop_err( 'Please check your annotation file is in correct format, GFF or GTF' ) + + #GFF = store_gff(options.gff) + #GFF = ribo_functions.store_gtf(options.gff) + ## check gff reading + if not GFF['order'] : + stop_err( 'Incorrect GFF file' ) + for gene in GFF['order']: + if not GFF[gene]['exon'] : + del GFF[gene] + GFF['order'].remove(gene) + + clean_file = ribo_functions.cleaning_bam(options.bamfile) + compute_analysis(clean_file, GFF, options.fasta, options.gff, subfolder, options.extend, options.kmer) + if os.path.exists( clean_file ): + os.remove( clean_file ) + + write_html_page(html_file,subfolder) + ##paste jquery script in result directory : + jq_src = os.path.join(os.path.dirname(__file__),'lightbox') + shutil.copytree(jq_src,os.path.join(subfolder,'lightbox')) + + + sys.stdout.write("Finish readthrough analysis at %s\n" % time.asctime( time.localtime(time.time()))) + except Exception, e: + stop_err( 'Error running metagene readthrough analysis : ' + str( e ) ) + + +if __name__=="__main__": + __main__()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ribo_tools/metagene_readthrough.xml Wed Sep 27 04:59:10 2017 -0400 @@ -0,0 +1,63 @@ +<tool id="readthrough_analysis" name="Stop_supp"> + <description>To analyse Ribo-seq alignments for the detection of stop codon readthrough events</description> + <requirements> + <requirement type="package">samtools</requirement> + <requirement type="python-module">HTseq</requirement> + <requirement type="python-module">pysam</requirement> + <requirement type="python-module">csv</requirement> + <requirement type="python-module">Bio</requirement> + </requirements> + <command interpreter="python"> + metagene_readthrough.py --gff $gff --fasta $fasta --bam $mapping --dirout=$output,$output.files_path --extend $ext + + </command> + + <inputs> + <param name="gff" type="data" label="Reference annotation file (GFF)" format="gff" /> + <param name="fasta" type="data" label="Reference genome in Fasta format" format="fasta" /> + <param name="mapping" type="data" label="Bam File" format="bam" /> + <param name="ext" type="integer" label="Length of 3’ UTR extension downstream the annotated stop codon (in bp)" value="300" /> + </inputs> + + <outputs> + <data format="html" name="output" label="[RP]Readthrough results on ${on_string}"/> + </outputs> + + <help> +Summary +------- +This tool uses Ribo-seq data (BAM file) to extract genes displaying potential stop codon readthrough events from a reference annotation file (`GFF3`_). + +C-terminal protein extensions were identified as previously described (Dunn J.G. et al, 2013). Only uniquely mapped footprints with a size in the 25 to 34 range are considered. +A gene is considered to display readthrough if: + + i) It is covered by more than 128 footprints. + + ii) There are footprints after the stop codon. + + iii) There are footprints overlapping the next in-frame stop codon. + + iv) There is no methionine codon in the next five codons downstream from the official stop codon of the CDS. + + v) Coverage is homogeneous within the extension. + +Stop codon readthrough was estimated by calculating the ratio of the number of footprints in the C-terminal extension to that in the CDS. Ribosome density footprints were estimated in RPKM (reads per kilobase per million). We controlled for variability due to stop codon peaks, by excluding footprints mapping to stop codons from the calculation of RPKM. + +.. _GFF3: http://gmod.org/wiki/GFF3 + + +Output +------- +This tool produces an html file with plots for each gene displaying readthrough. + + +Dependances +------------ + +.. class:: warningmark + +This tool depends on Python (>=2.7) and following packages : numpy 1.8.0, Biopython 1.58, matplotlib 1.3.1, HTSeq and pysam. Samtools is used for bam manipulation. + + </help> +</tool> +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ribo_tools/ribo_functions.py Wed Sep 27 04:59:10 2017 -0400 @@ -0,0 +1,372 @@ +#!/usr/bin/env python2.7 + +''' + Created on Jan. 2014 + @author: rachel legendre + @copyright: rachel.legendre@igmors.u-psud.fr + @license: GPL v3 +''' + +import sys, subprocess, re, commands, time, urllib, tempfile +from copy import copy + + +def stop_err( msg ): + sys.stderr.write( "%s\n" % msg ) + sys.stderr.write( "Programme aborted at %s\n" % time.asctime(time.localtime(time.time()))) + sys.exit() + +def split_bam(bamfile,tmpdir): + ''' + split bam by chromosome and write sam file in tmpdir + ''' + try: + #get header + results = subprocess.check_output(['samtools', 'view', '-H',bamfile]) + header = results.split('\n') + + #define genome size + genome = [] + for line in header: + result = re.search('SN', line) + if result : + #print line + feat = line.split('\t') + chrom = re.split(":", feat[1]) + #print feat[1] + genome.append(chrom[1]) + + #split sam by chrom + n = 0 + for chrm in genome: + with open(tmpdir+'/'+chrm+'.sam', 'w') as f : + #write header correctly for each chromosome + f.write(header[0]+'\n') + expr = re.compile(chrm+'\t') + el =[elem for elem in header if expr.search(elem)][0] + f.write(el+'\n') + f.write(header[-2]+'\n') + #write all reads for each chromosome + reads = subprocess.check_output(["samtools", "view", bamfile, chrm]) + f.write(reads) + # calculate number of reads + n += reads.count(chrm) + + sys.stdout.write("%d reads are presents in your bam file\n" % n) + + except Exception, e: + stop_err( 'Error during bam file splitting : ' + str( e ) ) + + + +def get_first_base(tmpdir, kmer, frame): + ''' + write footprint coverage file for each sam file in tmpdir + ''' + global total_mapped_read + ## tags by default + multi_tag = "XS:i:" + tag = "IH:i:1" + + try: + ### compute position of P-site according to frame (P-site -> +15 starting from 5' end of footprint) + p_site_pos = 16-frame + + file_array = (commands.getoutput('ls '+tmpdir)).split('\n') + ##write coverage for each sam file in tmpdir + for samfile in file_array: + with open(tmpdir+'/'+samfile, 'r') as sam : + ##get chromosome name + chrom = samfile.split(".sam")[0] + + for line in sam: + #initialize dictionnary + if '@SQ' in line : + size = int(line.split('LN:')[1]) + genomeF = [0]*size + genomeR = [0]*size + # define multiple reads keys from mapper + elif '@PG\tID' in line : + if 'bowtie' in line: + multi_tag = "XS:i:" + elif 'bwa' in line: + multi_tag = "XT:A:R" + elif 'TopHat' in line: + tag = "NH:i:1" + else : + stop_err("No PG tag find in "+samfile+". Please use bowtie, bwa or Tophat for mapping") + + # get footprint + elif re.search('^[^@].+', line) : + #print line.rstrip() + read_pos = int(line.split('\t')[3]) + read_sens = int(line.split('\t')[1]) + len_read = len(line.split('\t')[9]) + read_qual = int(line.split('\t')[4]) + if len_read == kmer and (tag in line or multi_tag not in line) and read_qual > 20 : + ###if line.split('\t')[5] == '28M' : + # print line.rstrip() + total_mapped_read +=1 + #if it's a forward read + if read_sens == 0 : + #get P-site : start read + 12 nt + #read_pos += 15 + read_pos += p_site_pos + genomeF[read_pos] += 1 + #if it's a reverse read + elif read_sens == 16 : + #get P-site + #read_pos += 12 + read_pos += (len_read-1) - p_site_pos + genomeR[read_pos] += 1 + + #try: + #write coverage in files + with open(tmpdir+'/assoCov_'+chrom+'.txt', 'w') as cov : + for i in range(0,size): + cov.write(str(genomeF[i])+'\t-'+str(genomeR[i])+'\n') + #except Exception,e: + # stop_err( 'Error during coverage file writting : ' + str( e ) ) + del genomeF + del genomeR + sys.stdout.write("%d reads are used for frame analysis\n" % total_mapped_read) + except Exception, e: + stop_err( 'Error during footprinting : ' + str( e ) ) + + + +def store_gff(gff): + ''' + parse and store gff file in a dictionnary + ''' + try: + GFF = {} + mRNA = {} + with open(gff, 'r') as f_gff : + + GFF['order'] = [] + #for line in itertools.islice( f_gff, 25 ): + for line in f_gff: + ## switch commented lines + line = line.split("#")[0] + if line != "" : + feature = (line.split('\t')[8]).split(';') + # first line is already gene line : + if line.split('\t')[2] == 'gene' : + gene = feature[0].replace("ID=","") + curent_gene = gene + if 'Name' in line : + regex = re.compile('(Name=)([^;]*);?') + res = regex.search(line.split('\t')[8]) + Name = res.group(2) + Name = Name.rstrip() + else : + Name = "Unknown" + ##get annotation + if 'Note' in line : + regex = re.compile('(Note=)([^;]*);?') + res = regex.search(line.split('\t')[8]) + note = res.group(2) + note = urllib.unquote(str(note)).replace("\n","") + else : + note = "" + ## store gene information + GFF['order'].append(gene) + GFF[gene] = {} + GFF[gene]['chrom'] = line.split('\t')[0] + GFF[gene]['start'] = int(line.split('\t')[3]) + GFF[gene]['stop'] = int(line.split('\t')[4]) + GFF[gene]['strand'] = line.split('\t')[6] + GFF[gene]['name'] = Name + GFF[gene]['note'] = note + GFF[gene]['exon'] = {} + GFF[gene]['exon_number'] = 0 + #print Name + elif line.split('\t')[2] == 'mRNA' : + regex = re.compile('(Parent=)([^;]*);?') + res = regex.search(line.split('\t')[8]) + gene_name = res.group(2) + regex = re.compile('(ID=)([^;]*);?') + res = regex.search(line.split('\t')[8]) + mRNA_name = res.group(2) + if gene not in mRNA.viewvalues() and gene_name == curent_gene : + mRNA[mRNA_name] = gene_name + + elif line.split('\t')[2] == 'CDS' : + regex = re.compile('(Parent=)([^;]*);?') + res = regex.search(line.split('\t')[8]) + gene = res.group(2) + + if 'mRNA' in gene: + gene = re.sub(r"(.*)(\_mRNA)", r"\1", gene) + if mRNA.has_key(gene) and GFF.has_key(mRNA[gene]): + + gene = gene_name + + if GFF.has_key(gene) : + GFF[gene]['exon_number'] += 1 + exon_number = GFF[gene]['exon_number'] + GFF[gene]['exon'][exon_number] = {} + GFF[gene]['exon'][exon_number]['frame'] = line.split('\t')[7] + GFF[gene]['exon'][exon_number]['start'] = int(line.split('\t')[3]) + GFF[gene]['exon'][exon_number]['stop'] = int(line.split('\t')[4]) + + ## if there is a five prim UTR intron, we change start of gene + elif line.split('\t')[2] == 'five_prime_UTR_intron' : + if GFF[gene]['strand'] == "+" : + GFF[gene]['start'] = GFF[gene]['exon'][1]['start'] + else : + GFF[gene]['stop'] = GFF[gene]['exon'][exon_number]['stop'] + return GFF + except Exception,e: + stop_err( 'Error during gff storage : ' + str( e ) ) + + +#chrI SGD gene 87286 87752 . + . ID=YAL030W;Name=YAL030W;gene=SNC1;Alias=SNC1;Ontology_term=GO:0005484,GO:0005768,GO:0005802,GO:0005886,GO:0005935,GO:0006887,GO:0006893,GO:000689 +#7,GO:0006906,GO:0030658,GO:0031201;Note=Vesicle%20membrane%20receptor%20protein%20%28v-SNARE%29%3B%20involved%20in%20the%20fusion%20between%20Golgi-derived%20secretory%20vesicles%20with%20the%20plasma%20membra +#ne%3B%20proposed%20to%20be%20involved%20in%20endocytosis%3B%20member%20of%20the%20synaptobrevin%2FVAMP%20family%20of%20R-type%20v-SNARE%20proteins%3B%20SNC1%20has%20a%20paralog%2C%20SNC2%2C%20that%20arose%20fr +#om%20the%20whole%20genome%20duplication;display=Vesicle%20membrane%20receptor%20protein%20%28v-SNARE%29;dbxref=SGD:S000000028;orf_classification=Verified +#chrI SGD CDS 87286 87387 . + 0 Parent=YAL030W_mRNA;Name=YAL030W_CDS;orf_classification=Verified +#chrI SGD CDS 87501 87752 . + 0 Parent=YAL030W_mRNA;Name=YAL030W_CDS;orf_classification=Verified +#chrI SGD intron 87388 87500 . + . Parent=YAL030W_mRNA;Name=YAL030W_intron;orf_classification=Verified + +def store_gtf(gff): + ''' + parse and store gtf file in a dictionnary + ''' + try: + GFF = {} + with open(gff, 'r') as f_gff : + GFF['order'] = [] + for line in f_gff: + ## switch commented lines + line = line.split("#")[0] + if line != "" : + # first line is already gene line : + if 'protein_coding' in line : + ##get name + gene = re.sub(r".+transcript_id \"([\w|-|\.]+)\";.*", r"\1", line).rstrip() + Name = re.sub(r".+gene_name \"([\w|\-|\:|\.|\(|\)]+)\";.*", r"\1", line).rstrip() + if line.split('\t')[2] == 'CDS' : + ##if its first time we get this gene + if gene not in GFF.keys() : + ## store gene information + GFF['order'].append(gene) + GFF[gene] = {} + GFF[gene]['chrom'] = line.split('\t')[0] + GFF[gene]['strand'] = line.split('\t')[6] + GFF[gene]['start'] = int(line.split('\t')[3]) + GFF[gene]['stop'] = int(line.split('\t')[4]) + GFF[gene]['name'] = Name + GFF[gene]['note'] = "" + GFF[gene]['exon_number'] = 1 + GFF[gene]['exon'] = {} + #exon_number = int(re.sub(r".+exon_number \"(\d+)\".+", r"\1",line).rstrip()) + ## some exons are non codant + exon_number = 1 + GFF[gene]['exon'][exon_number] = {} + GFF[gene]['exon'][exon_number]['start'] = int(line.split('\t')[3]) + GFF[gene]['exon'][exon_number]['stop'] = int(line.split('\t')[4]) + else : + ## we add exon + #exon_number = int(re.sub(r".+exon_number \"(\d+)\".+", r"\1",line).rstrip()) + exon_number += 1 + GFF[gene]['exon_number'] = exon_number + GFF[gene]['exon'][exon_number] = {} + GFF[gene]['exon'][exon_number]['start'] = int(line.split('\t')[3]) + GFF[gene]['exon'][exon_number]['stop'] = int(line.split('\t')[4]) + #elif line.split('\t')[2] == 'CDS' : + #exon_number = int(re.sub(r".+exon_number \"(\d+)\".+", r"\1",line).rstrip()) + GFF[gene]['exon'][exon_number]['frame'] = line.split('\t')[7] + elif line.split('\t')[2] == 'start_codon' : + if GFF[gene]['strand'] == '-' : + GFF[gene]['stop'] = int(line.split('\t')[4]) + else : + GFF[gene]['start'] = int(line.split('\t')[3]) + elif line.split('\t')[2] == 'stop_codon' : + if GFF[gene]['strand'] == '-' : + GFF[gene]['start'] = int(line.split('\t')[3]) + else : + GFF[gene]['stop'] = int(line.split('\t')[4]) + + return __reverse_coordinates__(GFF) + except Exception,e: + stop_err( 'Error during gtf storage : ' + str( e ) ) + + +##IV protein_coding exon 307766 307789 . - . gene_id "YDL083C"; transcript_id "YDL083C"; exon_number "1"; gene_name "RPS16B"; gene_biotype "protein_coding"; transcript_name "RPS16B"; +## exon_id "YDL083C.1"; +##IV protein_coding CDS 307766 307789 . - 0 gene_id "YDL083C"; transcript_id "YDL083C"; exon_number "1"; gene_name "RPS16B"; gene_biotype "protein_coding"; transcript_name "RPS16B"; +## protein_id "YDL083C"; +##IV protein_coding start_codon 307787 307789 . - 0 gene_id "YDL083C"; transcript_id "YDL083C"; exon_number "1"; gene_name "RPS16B"; gene_biotype "protein_coding"; transcript_name " +##RPS16B"; +##IV protein_coding exon 306926 307333 . - . gene_id "YDL083C"; transcript_id "YDL083C"; exon_number "2"; gene_name "RPS16B"; gene_biotype "protein_coding"; transcript_name "RPS16B"; +## exon_id "YDL083C.2"; +##IV protein_coding CDS 306929 307333 . - 0 gene_id "YDL083C"; transcript_id "YDL083C"; exon_number "2"; gene_name "RPS16B"; gene_biotype "protein_coding"; transcript_name "RPS16B"; +## protein_id "YDL083C"; +##IV protein_coding stop_codon 306926 306928 . - 0 gene_id "YDL083C"; transcript_id "YDL083C"; exon_number "2"; gene_name "RPS16B"; gene_biotype "protein_coding"; transcript_name " +##RPS16B"; +def __reverse_coordinates__(GFF): + + for gene in GFF['order']: + ## for reverse gene + if GFF[gene]['strand'] == "-": + ## if this gene have many exon and the stop of gene is the stop of first (and not last) exon, we reverse exon coordinates + if GFF[gene]['stop'] == GFF[gene]['exon'][1]['stop'] and GFF[gene]['exon_number'] > 1 : + tmp = copy(GFF[gene]['exon']) + exon_number = GFF[gene]['exon_number'] + rev_index = exon_number+1 + for z in range(1,exon_number+1): + rev_index -= 1 + GFF[gene]['exon'][z] = tmp[rev_index] + + ## check start + if GFF[gene]['start'] != GFF[gene]['exon'][1]['start'] and GFF[gene]['start']: + GFF[gene]['exon'][1]['start'] = GFF[gene]['start'] + + return GFF + + +def cleaning_bam(bam): + ''' + Remove reads unmapped, non uniquely mapped and reads with length lower than 25 and upper than 32, and mapping quality upper than 12 + ''' + try : + header = subprocess.check_output(['samtools', 'view', '-H', bam], stderr= subprocess.PIPE) + #header = results.split('\n') + ## tags by default + multi_tag = "XS:i:" + tag = "IH:i:1" + # check mapper for define multiple tag + if 'bowtie' in header: + multi_tag = "XS:i:" + elif 'bwa' in header: + multi_tag = "XT:A:R" + elif 'TopHat' in header: + tag = "NH:i:1" + else : + stop_err("No PG tag find in "+samfile+". Please use bowtie, bwa or Tophat for mapping") + + tmp_sam = tempfile.mktemp() + cmd = "samtools view %s > %s" % (bam, tmp_sam) + proc = subprocess.Popen( args=cmd, shell=True, stderr = subprocess.PIPE) + returncode = proc.wait() + + + with open(tempfile.mktemp(),'w') as out : + out.write(header) + with open(tmp_sam,'r') as sam : + for line in sam : + if (multi_tag not in line or tag in line) and line.split('\t')[1] != '4' and int(line.split('\t')[4]) > 12 : + if len(line.split('\t')[9]) < 32 and len(line.split('\t')[9]) > 25 : + out.write(line) + bamfile = tempfile.mktemp()+'.bam' + cmd = "samtools view -hSb %s > %s" % (out.name,bamfile) + proc = subprocess.Popen( args=cmd, shell=True, stderr = subprocess.PIPE) + returncode = proc.wait() + + return bamfile + + except Exception,e: + stop_err( 'Error during cleaning bam : ' + str( e ) ) + \ No newline at end of file