Ando et al. An optical marker based on the UV-induced green-to-red photoconversion of a fluorescent protein. Proceedings of the National Academy of Sciences 2002. 99(20): 12651-12656. doi: 10.1073/pnas.202320599
2 |
Tsutsui et al. Semi-rational engineering of a coral fluorescent protein into an efficient highlighter. EMBO Rep 2005. 6(3): 233-238. doi: 10.1038/sj.embor.7400361
3 |
McKinney et al. A bright and photostable photoconvertible fluorescent protein. Nat Meth 2009. 6(2): 131-133. doi: 10.1038/nmeth.1296
4 |
Zhang et al. Rational design of true monomeric and bright photoactivatable fluorescent proteins. Nat Meth 2012. 9(7): 727-729. doi: 10.1038/nmeth.2021
5 |
Subach et al. A photoswitchable orange-to-far-red fluorescent protein, PSmOrange. Nat Meth 2011. 8(9): 771-777. doi: 10.1038/nmeth.1664
6 |
Patterson A Photoactivatable GFP for Selective Photolabeling of Proteins and Cells. Science 2002. 297(5588): 1873-1877. doi: 10.1126/science.1074952
7 |
Subach et al. Photoactivatable mCherry for high-resolution two-color fluorescence microscopy. Nat Meth 2009. 6(2): 153-159. doi: 10.1038/nmeth.1298
8 |
Subach et al. Bright Monomeric Photoactivatable Red Fluorescent Protein for Two-Color Super-Resolution sptPALM of Live Cells. J. Am. Chem. Soc. 2010. 132(18): 6481-6491. doi: 10.1021/ja100906g
9 |
McEvoy et al. mMaple: A Photoconvertible Fluorescent Protein for Use in Multiple Imaging Modalities. PLoS ONE 2012. 7(12): e51314. doi: 10.1371/journal.pone.0051314
10 |
Brakemann et al. A reversibly photoswitchable GFP-like protein with fluorescence excitation decoupled from switching. Nat Biotechnol 2011. 29(10): 942-947. doi: 10.1038/nbt.1952
11 |
Grotjohann et al. Diffraction-unlimited all-optical imaging and writing with a photochromic GFP. Nature 2011. 478(7368): 204-208. doi: 10.1038/nature10497
12 |
Gunewardene et al. Superresolution Imaging of Multiple Fluorescent Proteins with Highly Overlapping Emission Spectra in Living Cells. Biophysical Journal 2011. 101(6): 1522-1528. doi: 10.1016/j.bpj.2011.07.049
13 |
Subach et al. A FRET-Facilitated Photoswitching Using an Orange Fluorescent Protein with the Fast Photoconversion Kinetics. J. Am. Chem. Soc. 2012. 134(36): 14789-14799. doi: 10.1021/ja3034137
14 |
Fuchs et al. A photoactivatable marker protein for pulse-chase imaging with superresolution. Nat Meth 2010. 7(8): 627-630. doi: 10.1038/nmeth.1477
15 |
Hoi et al. A Monomeric Photoconvertible Fluorescent Protein for Imaging of Dynamic Protein Localization. Journal of Molecular Biology 2010. 401(5): 776-791. doi: 10.1016/j.jmb.2010.06.056
16 |
Habuchi et al. mKikGR, a Monomeric Photoswitchable Fluorescent Protein. PLoS ONE 2008. 3(12): e3944. doi: 10.1371/journal.pone.0003944
17 |
Ando Regulated Fast Nucleocytoplasmic Shuttling Observed by Reversible Protein Highlighting. Science 2004. 306(5700): 1370-1373. doi: 10.1126/science.1102506
18 |
Chang et al. A unique series of reversibly switchable fluorescent proteins with beneficial properties for various applications. Proceedings of the National Academy of Sciences 2012. 109(12): 4455-4460. doi: 10.1073/pnas.1113770109
19 |
Grotjohann et al. rsEGFP2 enables fast RESOLFT nanoscopy of living cells. eLife 2012. 1(0): e00248-e00248. doi: 10.7554/elife.00248
20 |
Moeyaert et al. Green-to-Red Photoconvertible Dronpa Mutant for Multimodal Super-resolution Fluorescence Microscopy. ACS Nano 2014. 8(2): 1664-1673. doi: 10.1021/nn4060144
Each fluorescent protein state is initially plotted with excitation wavelength on the x-axis and emission wavelength on the y-axis. The color is set
72 | based on its emission wavelength, and fades to gray as the brightness (product of exctinction coefficient and quantum yield) decreases.
73 | Photoconvertible proteins (e.g. green to red) are drawn as sqaures, with the different states connected by arrows. The color of the arrow corresponds to the switching wavelength.
74 | Photoactivatible proteins (e.g. off to on) are drawn as circles; the circle boundary is colored according to the switching wavelength. Dashed boundaries indicate
75 | proteins that are reversibly photoswitchable (e.g. off to on to off).
76 | Mouseover each circle to see info on that protein or click on any datapoint to see the corresponding reference on PubMed. You can zoom using the mouse scroll wheel and pan by clicking and dragging. Use the filter sliders at the top right to select a subset of fluorescent proteins based on certain criteria. Use the X and Y axis toggle boxes to change what is plotted on each axis.
Each fluorescent protein begins plotted with excitation wavelength on the x-axis and emission wavelength on the y-axis. The color is set
74 | based on its emission wavelength, and fades to gray as the brightness (product of exctinction coefficient and quantum yield) decreases.
75 | Intrinsically fluorescent proteins are shown as circles and proteins requiring an extrinisc cofactor are shown as squares. Dimeric proteins are
76 | indicated with a 2 inside the symbol; tandem dimeric proteins are shown with a t inside the symbol.
77 | Mouseover each circle to see info on that protein or click on any datapoint to see the corresponding reference on PubMed. You can zoom using the mouse scroll wheel and pan by clicking and dragging. Use the filter sliders at the top right to select a subset of fluorescent proteins based on certain criteria. Use the X and Y axis toggle boxes to change what is plotted on each axis.
Tomosugi et al. An ultramarine fluorescent protein with increased photostability and pH insensitivity. Nature Methods 2009. 6(5): 351-353. doi: 10.1038/nmeth.1317
2 |
Mena et al. Blue fluorescent proteins with enhanced brightness and photostability from a structurally targeted library. Nature Biotechnology 2006. 24(12): 1569-1571. doi: 10.1038/nbt1264
3 |
Subach et al. Conversion of Red Fluorescent Protein into a Bright Blue Probe. Chemistry & Biology 2008. 15(10): 1116-1124. doi: 10.1016/j.chembiol.2008.08.006
4 |
Ai et al. Exploration of New Chromophore Structures Leads to the Identification of Improved Blue Fluorescent Proteins†. Biochemistry 2007. 46(20): 5904-5910. doi: 10.1021/bi700199g
5 |
Subach et al. An Enhanced Monomeric Blue Fluorescent Protein with the High Chemical Stability of the Chromophore. PLoS ONE 2011. 6(12): e28674. doi: 10.1371/journal.pone.0028674
6 |
Cubitt et al. Chapter 2: Understanding Structure—Function Relationships in the Aequorea victoria Green Fluorescent Protein. Methods in Cell Biology 1998.: 19-30. doi: 10.1016/s0091-679x(08)61946-9
Heim et al. Wavelength mutations and posttranslational autoxidation of green fluorescent protein.. Proceedings of the National Academy of Sciences 1994. 91(26): 12501-12504. doi: 10.1073/pnas.91.26.12501
9 |
Rizzo et al. An improved cyan fluorescent protein variant useful for FRET. Nature Biotechnology 2004. 22(4): 445-449. doi: 10.1038/nbt945
10 |
Markwardt et al. An Improved Cerulean Fluorescent Protein with Enhanced Brightness and Reduced Reversible Photoswitching. PLoS ONE 2011. 6(3): e17896. doi: 10.1371/journal.pone.0017896
11 |
Kremers et al. Cyan and Yellow Super Fluorescent Proteins with Improved Brightness, Protein Folding, and FRET Förster Radius†,‡. Biochemistry 2006. 45(21): 6570-6580. doi: 10.1021/bi0516273
12 |
Nguyen et al. Evolutionary optimization of fluorescent proteins for intracellular FRET. Nature Biotechnology 2005. 23(3): 355-360. doi: 10.1038/nbt1066
13 |
Goedhart et al. Bright cyan fluorescent protein variants identified by fluorescence lifetime screening. Nature Methods 2010. 7(2): 137-139. doi: 10.1038/nmeth.1415
14 |
Goedhart et al. Structure-guided evolution of cyan fluorescent proteins towards a quantum yield of 93%. Nature Communications 2012. 3: 751. doi: 10.1038/ncomms1738
15 |
Kogure et al. A fluorescent variant of a protein from the stony coral Montipora facilitates dual-color single-laser fluorescence cross-correlation spectroscopy. Nature Biotechnology 2006. 24(5): 577-581. doi: 10.1038/nbt1207
16 |
Yang et al. mBeRFP, an Improved Large Stokes Shift Red Fluorescent Protein. PLoS ONE 2013. 8(6): e64849. doi: 10.1371/journal.pone.0064849
17 |
Piatkevich et al. Monomeric red fluorescent proteins with a large Stokes shift. Proceedings of the National Academy of Sciences 2010. 107(12): 5369-5374. doi: 10.1073/pnas.0914365107
18 |
Ai et al. Directed evolution of a monomeric, bright and photostable version of Clavularia cyan fluorescent protein: structural characterization and applications in fluorescence imaging . Biochemical Journal 2006. 400(3): 531-540. doi: 10.1042/bj20060874
19 |
KARASAWA et al. Cyan-emitting and orange-emitting fluorescent proteins as a donor/acceptor pair for fluorescence resonance energy transfer. Biochemical Journal 2004. 381(1): 307-312. doi: 10.1042/bj20040321
20 |
Evdokimov et al. Structural basis for the fast maturation of Arthropoda green fluorescent protein. EMBO reports 2006. 7(10): 1006-1012. doi: 10.1038/sj.embor.7400787
21 |
Tsutsui et al. Improving membrane voltage measurements using FRET with new fluorescent proteins. Nature Methods 2008. 5(8): 683-685. doi: 10.1038/nmeth.1235
22 |
Pédelacq et al. Engineering and characterization of a superfolder green fluorescent protein. Nature Biotechnology 2005. 24(1): 79-88. doi: 10.1038/nbt1172
23 |
Yang et al. Optimized Codon Usage and Chromophore Mutations Provide Enhanced Sensitivity with the Green Fluorescent Protein. Nucleic Acids Research 1996. 24(22): 4592-4593. doi: 10.1093/nar/24.22.4592
24 |
Sarkisyan et al. Green Fluorescent Protein with Anionic Tryptophan-Based Chromophore and Long Fluorescence Lifetime. Biophysical Journal 2015. 109(2): 380-389. doi: 10.1016/j.bpj.2015.06.018
25 |
Karasawa et al. A Green-emitting Fluorescent Protein from Galaxeidae Coral and Its Monomeric Version for Use in Fluorescent Labeling. Journal of Biological Chemistry 2003. 278(36): 34167-34171. doi: 10.1074/jbc.m304063200
26 |
Ai et al. Hue-shifted monomeric variants of Clavularia cyan fluorescent protein: identification of the molecular determinants of color and applications in fluorescence imaging. BMC Biology 2008. 6(1): 13. doi: 10.1186/1741-7007-6-13
27 |
Lam et al. Improving FRET dynamic range with bright green and red fluorescent proteins. Nature Methods 2012. 9(10): 1005-1012. doi: 10.1038/nmeth.2171
28 |
Shaner et al. A bright monomeric green fluorescent protein derived from Branchiostoma lanceolatum. Nature Methods 2013. 10(5): 407-409. doi: 10.1038/nmeth.2413
29 |
Orm et al. Crystal Structure of the Aequorea victoria Green Fluorescent Protein. Science 1996. 273(5280): 1392-1395. doi: 10.1126/science.273.5280.1392
30 |
Nagai et al. A variant of yellow fluorescent protein with fast and efficient maturation for cell-biological applications. Nature Biotechnology 2002. 20(1): 87-90. doi: 10.1038/nbt0102-87
31 |
Griesbeck et al. Reducing the Environmental Sensitivity of Yellow Fluorescent Protein: MECHANISM AND APPLICATIONS. Journal of Biological Chemistry 2001. 276(31): 29188-29194. doi: 10.1074/jbc.m102815200
32 |
Shaner et al. Improved monomeric red, orange and yellow fluorescent proteins derived from Discosoma sp. red fluorescent protein. Nature Biotechnology 2004. 22(12): 1567-1572. doi: 10.1038/nbt1037
33 |
Shaner et al. Improving the photostability of bright monomeric orange and red fluorescent proteins. Nature Methods 2008. 5(6): 545-551. doi: 10.1038/nmeth.1209
34 |
Dean et al. High-Speed Multiparameter Photophysical Analyses of Fluorophore Libraries. Analytical Chemistry 2015. 87(10): 5026-5030. doi: 10.1021/acs.analchem.5b00607
35 |
Sakaue-Sawano et al. Visualizing Spatiotemporal Dynamics of Multicellular Cell-Cycle Progression. Cell 2008. 132(3): 487-498. doi: 10.1016/j.cell.2007.12.033
36 |
Merzlyak et al. Bright monomeric red fluorescent protein with an extended fluorescence lifetime. Nature Methods 2007. 4(7): 555-557. doi: 10.1038/nmeth1062
37 |
Bindels et al. mScarlet: a bright monomeric red fluorescent protein for cellular imaging. Nature Methods 2016. 14(1): 53-56. doi: 10.1038/nmeth.4074
38 |
Kredel et al. mRuby, a Bright Monomeric Red Fluorescent Protein for Labeling of Subcellular Structures. PLoS ONE 2009. 4(2): e4391. doi: 10.1371/journal.pone.0004391
39 |
Shemiakina et al. A monomeric red fluorescent protein with low cytotoxicity. Nature Communications 2012. 3: 1204. doi: 10.1038/ncomms2208
40 |
Shcherbo et al. Far-red fluorescent tags for protein imaging in living tissues. Biochemical Journal 2009. 418(3): 567-574. doi: 10.1042/bj20081949
41 |
Wang et al. Evolution of new nonantibody proteins via iterative somatic hypermutation. Proceedings of the National Academy of Sciences 2004. 101(48): 16745-16749. doi: 10.1073/pnas.0407752101
42 |
Lin et al. Autofluorescent Proteins with Excitation in the Optical Window for Intravital Imaging in Mammals. Chemistry & Biology 2009. 16(11): 1169-1179. doi: 10.1016/j.chembiol.2009.10.009
Morozova et al. Far-Red Fluorescent Protein Excitable with Red Lasers for Flow Cytometry and Superresolution STED Nanoscopy. Biophysical Journal 2010. 99(2): L13-L15. doi: 10.1016/j.bpj.2010.04.025
45 |
Shu et al. Mammalian Expression of Infrared Fluorescent Proteins Engineered from a Bacterial Phytochrome. Science 2009. 324(5928): 804-807. doi: 10.1126/science.1168683
46 |
Filonov et al. Bright and stable near-infrared fluorescent protein for in vivo imaging. Nature Biotechnology 2011. 29(8): 757-761. doi: 10.1038/nbt.1918
47 |
Shcherbakova et al. Near-infrared fluorescent proteins for multicolor in vivo imaging. Nature Methods 2013. 10(8): 751-754. doi: 10.1038/nmeth.2521
48 |
Yang et al. Time-resolved flow cytometry for lifetime measurements of near-infrared fluorescent proteins. Conference on Lasers and Electro-Optics 2016.doi: 10.1364/cleo_si.2016.sw4g.1
49 |
Piatkevich et al. Extended Stokes Shift in Fluorescent Proteins: Chromophore–Protein Interactions in a Near-Infrared TagRFP675 Variant. Scientific Reports 2013. 3doi: 10.1038/srep01847
50 |
Ai et al. Fluorescent protein FRET pairs for ratiometric imaging of dual biosensors. Nature Methods 2008. 5(5): 401-403. doi: 10.1038/nmeth.1207
51 |
Johnson et al. Red Fluorescent Protein pH Biosensor to Detect Concentrative Nucleoside Transport. Journal of Biological Chemistry 2009. 284(31): 20499-20511. doi: 10.1074/jbc.m109.019042
52 |
Pletnev et al. Structure of the red fluorescent protein from a lancelet (Branchiostoma lanceolatum): a novel GYG chromophore covalently bound to a nearby tyrosine. Acta Crystallographica Section D Biological Crystallography 2013. 69(9): 1850-1860. doi: 10.1107/s0907444913015424
53 |
Shcherbakova et al. An Orange Fluorescent Protein with a Large Stokes Shift for Single-Excitation Multicolor FCCS and FRET Imaging. Journal of the American Chemical Society 2012. 134(18): 7913-7923. doi: 10.1021/ja3018972
54 |
Hoi et al. An Engineered Monomeric Zoanthus sp. Yellow Fluorescent Protein. Chemistry & Biology 2013. 20(10): 1296-1304. doi: 10.1016/j.chembiol.2013.08.008
55 |
Erard et al. Minimum set of mutations needed to optimize cyan fluorescent proteins for live cell imaging. Mol. BioSyst. 2013. 9(2): 258-267. doi: 10.1039/c2mb25303h
56 |
Chu et al. Non-invasive intravital imaging of cellular differentiation with a bright red-excitable fluorescent protein. Nature Methods 2014. 11(5): 572-578. doi: 10.1038/nmeth.2888
57 |
Yu et al. An improved monomeric infrared fluorescent protein for neuronal and tumour brain imaging. Nature Communications 2014. 5doi: 10.1038/ncomms4626
58 |
Yu et al. A naturally monomeric infrared fluorescent protein for protein labeling in vivo. Nature Methods 2015. 12(8): 763-765. doi: 10.1038/nmeth.3447
59 |
Bajar et al. Improving brightness and photostability of green and red fluorescent proteins for live cell imaging and FRET reporting. Scientific Reports 2016. 6: 20889. doi: 10.1038/srep20889
60 |
Chu et al. A bright cyan-excitable orange fluorescent protein facilitates dual-emission microscopy and enhances bioluminescence imaging in vivo. Nature Biotechnology 2016. 34(7): 760-767. doi: 10.1038/nbt.3550
61 |
Rodriguez et al. A far-red fluorescent protein evolved from a cyanobacterial phycobiliprotein. Nature Methods 2016. 13(9): 763-769. doi: 10.1038/nmeth.3935
62 |
Shcherbakova et al. Bright monomeric near-infrared fluorescent proteins as tags and biosensors for multiscale imaging. Nature Communications 2016. 7: 12405. doi: 10.1038/ncomms12405
63 |
Ren et al. Cysteine Sulfoxidation Increases the Photostability of Red Fluorescent Proteins. ACS Chemical Biology 2016. 11(10): 2679-2684. doi: 10.1021/acschembio.6b00579
64 |
Ghosh et al. Blue protein with red fluorescence. Proceedings of the National Academy of Sciences 2016. 113(41): 11513-11518. doi: 10.1073/pnas.1525622113
65 |
Laviv et al. Simultaneous dual-color fluorescence lifetime imaging with novel red-shifted fluorescent proteins. Nature Methods 2016. 13(12): 989-992. doi: 10.1038/nmeth.4046
66 |
Bajar et al. Fluorescent indicators for simultaneous reporting of all four cell cycle phases. Nature Methods 2016. 13(12): 993-996. doi: 10.1038/nmeth.4045
67 |
Matela et al. A far-red emitting fluorescent marker protein, mGarnet2, for microscopy and STED nanoscopy. Chem. Commun. 2017.doi: 10.1039/c6cc09081h
68 |
--------------------------------------------------------------------------------
/js/fpvis.js:
--------------------------------------------------------------------------------
1 | //global variables to hold the current variables plotted on each axis
2 | var currentX = "lambda_ex"
3 | var currentY = "lambda_em"
4 | var symbolsize = 8; //radius of circle
5 | var bigscale = 1.5; //how much to scale up on mouseover
6 | //global varable to set the ranges over which the data is filtered.
7 | var filters = {
8 | "lambda_ex" : [350,800,1], // array values represent [min range, max range, step (for the range slider)]
9 | "lambda_em" : [350,800,1],
10 | "E" : [10000,170000,1000],
11 | "QY" : [0,1,0.01],
12 | "brightness": [0,100,1]
13 | }
14 | //string variables for updating the axis labels
15 | var strings = {
16 | "lambda_em" : "Emission Wavelength (nm)",
17 | "lambda_ex" : "Excitation Wavelength (nm)",
18 | "stokes" : "Stokes Shift (nm)",
19 | "E" : "Extinction Coefficient",
20 | "QY" : "Quantum Yield",
21 | "brightness": "Brightness",
22 | "pka" : "pKa",
23 | "bleach" : "Bleaching Half-life (s)",
24 | "mature" : "Maturation Half-time (min)",
25 | "lifetime" : "Lifetime (ns)",
26 | }
27 |
28 | //shorter strings for the table
29 | var tableStrings = {
30 | "Name" : "Protein",
31 | "lambda_ex" : "λex (nm)",
32 | "lambda_em" : "λem (nm)",
33 | "E" : "EC",
34 | "QY" : "QY",
35 | "brightness": "Brightness",
36 | "pka" : "pKa",
37 | "bleach" : "Bleaching (s)",
38 | "mature" : "Maturation (min)",
39 | "lifetime" : "Lifetime (ns)",
40 | "RefNum" : "Reference"
41 | }
42 |
43 | //Protein classes for tables
44 | var FPgroups = [
45 | {"Name" : "UV", "ex_min" : 0, "ex_max" : 380, "em_min" : 0, "em_max" : 1000, "color" : "#C080FF"},
46 | {"Name" : "Blue", "ex_min" : 380, "ex_max" : 421, "em_min" : 0, "em_max" : 470, "color" : "#8080FF"},
47 | {"Name" : "Cyan", "ex_min" : 421, "ex_max" : 473, "em_min" : 0, "em_max" : 530, "color" : "#80FFFF"},
48 | {"Name" : "Green", "ex_min" : 473, "ex_max" : 507, "em_min" : 480, "em_max" : 530, "color" : "#80FF80"},
49 | {"Name" : "Yellow", "ex_min" : 507, "ex_max" : 531, "em_min" : 500, "em_max" : 1000, "color" : "#FFFF80"},
50 | {"Name" : "Orange", "ex_min" : 531, "ex_max" : 555, "em_min" : 530, "em_max" : 569, "color" : "#FFC080"},
51 | {"Name" : "Red", "ex_min" : 555, "ex_max" : 600, "em_min" : 570, "em_max" : 620, "color" : "#FFA080"},
52 | {"Name" : "Far Red", "ex_min" : 585, "ex_max" : 631, "em_min" : 620, "em_max" : 1000, "color" : "#FF8080"},
53 | {"Name" : "Near IR", "ex_min" : 631, "ex_max" : 800, "em_min" : 661, "em_max" : 1000, "color" : "#B09090"},
54 | {"Name" : "Sapphire-type", "ex_min" : 380, "ex_max" : 420, "em_min" : 480, "em_max" : 530, "color" : "#8080FF"},
55 | {"Name" : "Long Stokes Shift", "ex_min" : 350, "ex_max" : 500, "em_min" : 560, "em_max" : 650, "color" : "#80A0FF"}
56 | ]
57 |
58 | //on page load, listen to slider events and respond by updating the filter ranges (and updating the ui)
59 | //this uses jQuery and jQuery UI which have been added to the head of the document.
60 | $(function() {
61 |
62 | //dynamically generate filter sliders based on "filters" object
63 | $.each(filters, function(i,v){
64 | var label = $("").appendTo("#sliders");
65 | var slider = $("").appendTo("#sliders");
66 |
67 | slider.rangeSlider({
68 | bounds:{min: v[0], max: v[1]},
69 | defaultValues:{min: v[0], max: v[1]},
70 | step: v[2],
71 | arrows: false,
72 | formatter:function(val){
73 | return (Math.round(val * 100) / 100);
74 | }
75 | });
76 | });
77 |
78 | // update filter settings when user changes slider
79 | $(".rangeSlider").on("valuesChanging", function(e, data){
80 | var filtID = $(this).attr('id');
81 | filters[filtID][0] = data.values.min;
82 | filters[filtID][1] = data.values.max;
83 | plot();
84 | });
85 |
86 | $("#Xradio").buttonsetv();
87 | $("#Yradio").buttonsetv();
88 |
89 | $( "#Xradio input" ).click(function() {
90 | currentX = $(this).val();
91 | plot();
92 | });
93 | $( "#Yradio input" ).click(function() {
94 | currentY = $(this).val();
95 | plot();
96 | });
97 |
98 | //easter egg
99 | $("#doalittledance").click(function(){doalittledance(1600);});
100 | });
101 |
102 | //load the bibliography
103 | $("#bibliography").load('FPs_bibliography.html');
104 |
105 | // Chart dimensions.
106 | var margin = {top: 20, right: 30, bottom: 20, left: 50},
107 | width = 700 - margin.right,
108 | height = 700 - margin.top - margin.bottom;
109 |
110 | //Scales and axes
111 | var xScale = d3.scale.linear()
112 | .range ([0, width]);
113 |
114 | var yScale = d3.scale.linear()
115 | .range ([height, 0]);
116 |
117 | //This scale will set the saturation (gray to saturated color). We will use it for mapping brightness.
118 | var saturationScale = d3.scale.linear()
119 | .range([0, 1])
120 | .domain([0, 100]);
121 |
122 | //This scale will set the hue. We will use it for mapping emission wavelength.
123 | var hueScale = d3.scale.linear()
124 | .range([300, 300, 240, 0, 0])
125 | .domain([200, 405, 440, 650, 850]);
126 |
127 | //X and Y axes
128 | var xAxis_bottom = d3.svg.axis().scale(xScale).tickSize(5).tickSubdivide(true);
129 | var yAxis_left = d3.svg.axis().scale(yScale).tickSize(5).orient("left").tickSubdivide(true);
130 |
131 | //top and right axes are identical but without tick labels
132 | var xAxis_top = d3.svg.axis().scale(xScale).tickSize(5).orient("top").tickSubdivide(true).tickFormat(function (d) { return ''; });;;
133 | var yAxis_right = d3.svg.axis().scale(yScale).tickSize(5).orient("right").tickSubdivide(true).tickFormat(function (d) { return ''; });;
134 |
135 | // Create the SVG container and set the origin.
136 | var svg = d3.select("#graph").append("svg")
137 | .attr("width", width + margin.left + margin.right)
138 | .attr("height", height + margin.top + margin.bottom)
139 | .append("g")
140 | .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
141 |
142 | //Add the axes
143 | svg.append("g")
144 | .attr("class", "x axis bottom")
145 | .attr("transform", "translate(0," + height + ")")
146 | .call(xAxis_bottom);
147 | svg.append("g")
148 | .attr("class", "y axis left")
149 | .call(yAxis_left);
150 | svg.append("g")
151 | .attr("class", "x axis top")
152 | .call(xAxis_top);
153 | svg.append("svg:g")
154 | .attr("class", "y axis right")
155 | .attr("transform", "translate(" + width + ",0)")
156 | .call(yAxis_right);
157 |
158 | // Add an x-axis label.
159 | svg.append("text")
160 | .attr("class", "x label")
161 | .attr("text-anchor", "middle")
162 | .attr("x", width/2 )
163 | .attr("y", height-10)
164 | .text("Excitation wavelength (nm)");
165 |
166 | // Add a y-axis label.
167 | svg.append("text")
168 | .attr("class", "y label")
169 | .attr("text-anchor", "middle")
170 | .attr("x", -height/2)
171 | .attr("y", margin.left-30)
172 | .attr("transform", "rotate(-90)")
173 | .text("Emission wavelength (nm)");
174 |
175 | //Add a clipping path so that data points don't go outside of frame
176 | svg.append("clipPath") //Make a new clipPath
177 | .attr("id", "chart-area") //Assign an ID
178 | .append("rect")
179 | .attr("width", width)
180 | .attr("height", height);
181 |
182 | //enable zooming
183 | var zoom = d3.behavior.zoom()
184 | .x(xScale)
185 | .y(yScale)
186 | .scaleExtent([1, 10])
187 | .on("zoom", draw_graph);
188 |
189 | function plotcircle(sel){
190 | circle = sel.append("circle")
191 | .attr("class", "FP")
192 | .attr("r", symbolsize)
193 | .attr("stroke", "#000")
194 | .attr("opacity", 0.7)
195 | .style("fill", function (d) { return d3.hsl(hueScale (d.lambda_em), saturationScale (d.brightness), 0.5)});
196 | addactions(circle);
197 | }
198 |
199 | function plotsquare(sel){
200 | square = sel.append("rect")
201 | .attr("class", "FP")
202 | .attr("width", symbolsize*2)
203 | .attr("height", symbolsize*2)
204 | .attr("stroke", "#000")
205 | .attr("opacity", 0.7)
206 | .style("fill", function (d) { return d3.hsl(hueScale (d.lambda_em), saturationScale (d.brightness), 0.5)});
207 | addactions(square);
208 | }
209 |
210 | function plottext(sel){
211 | text = sel.append("text")
212 | .attr("class", "FP")
213 | .text(function (d) {
214 | if (d["agg"] == "d") { return "2"}
215 | else if (d["agg"] == "td") { return "t"}
216 | else if (d["agg"] == "t") { return "4"}
217 | ;} )
218 | }
219 |
220 | function addactions(sel){
221 | // sel.on('click', function(e){
222 | // if(e.DOI){window.location = "http://dx.doi.org/" + e.DOI;}
223 | // })
224 | sel.on("mouseover", function(d) {
225 | //Get this bar's x/y values, then augment for the tooltip
226 | if (d3.select(this).attr("cx")){ //if circle
227 | d3.select(this).transition().duration(100).attr("r",symbolsize*bigscale);
228 | var xPosition = parseFloat(d3.select(this).attr("cx"))
229 | var yPosition = parseFloat(d3.select(this).attr("cy"))
230 | } else if (d3.select(this).attr("x")){ //if rectangle
231 | d3.select(this).transition().duration(100)
232 | .attr("x", function (d) { return xScale (d[currentX]) - symbolsize*bigscale; })
233 | .attr("y", function (d) { return yScale (d[currentY]) - symbolsize*bigscale; })
234 | .attr("width", symbolsize*2*bigscale)
235 | .attr("height", symbolsize*2*bigscale);
236 | var xPosition = parseFloat(d3.select(this).attr("x") )
237 | var yPosition = parseFloat(d3.select(this).attr("y") )
238 | }
239 | if (xPosition520){
245 | yPosition =520;
246 | }
247 | //Update the tooltip position and value
248 | d3.select("#tooltip")
249 | .style("left", xPosition + "px")
250 | .style("top", yPosition + "px")
251 | .select("#exvalue")
252 | .text(d.lambda_ex)
253 | d3.select("#tooltip")
254 | .select("#emvalue")
255 | .text(d.lambda_em);
256 | d3.select("#tooltip")
257 | .select("#ecvalue")
258 | .text(d.E);
259 | d3.select("#tooltip")
260 | .select("#qyvalue")
261 | .text(d.QY);
262 | d3.select("#tooltip")
263 | .select("h3")
264 | .html(d.Name);
265 | d3.select("#tooltip")
266 | .select("#brightnessvalue")
267 | .text(d.brightness);
268 |
269 | //Show the tooltip
270 | d3.select("#tooltip").classed("hidden", false);
271 | })
272 |
273 | .on("mouseout", function() {
274 | if (d3.select(this).attr("cx")){ //if circle
275 | d3.select(this).transition().duration(200).attr("r",symbolsize)
276 | } else if (d3.select(this).attr("x")){ //if circle
277 | d3.select(this).transition().duration(200)
278 | .attr("x", function (d) { return xScale (d[currentX]) - symbolsize; })
279 | .attr("y", function (d) { return yScale (d[currentY]) - symbolsize; })
280 | .attr("width", symbolsize*2)
281 | .attr("height", symbolsize*2);
282 | }
283 | //Hide the tooltip
284 | d3.select("#tooltip").classed("hidden", true);
285 | })
286 | }
287 |
288 | svg.append("rect")
289 | .attr("class", "pane")
290 | .attr("width", width)
291 | .attr("height", height)
292 | .call(zoom);
293 |
294 | var FPdata = []; //Where the fluorescent protein data table will end up.
295 |
296 | // load the csv file and plot it
297 | d3.csv("FPs_processed.csv", function (data) {
298 | data.forEach(function(d){
299 | d.lambda_em = +d.lambda_em; // typing these variables here for simplicity of code later on
300 | d.lambda_ex = +d.lambda_ex;
301 | d.E = +d.E;
302 | d.QY = +d.QY;
303 | d.brightness = +d.brightness;
304 | })
305 |
306 | FPdata = data;
307 |
308 | //Only update max of saturation scale, so that gray corresponds to 0 brightness
309 | //Use 80th percentile as max saturation so that not everything is muddy gray
310 | saturationScale.domain([0,
311 | d3.quantile(FPdata.map(function(a) {return (+a.brightness)}).sort(function(a,b){return a-b}),0.8)
312 | ]);
313 |
314 | plot();
315 | draw_table();
316 | });
317 |
318 | function draw_graph(){
319 | //redraw axes with new domains
320 | svg.select(".x.axis.bottom").call(xAxis_bottom);
321 | svg.select(".y.axis.left").call(yAxis_left);
322 | svg.select(".x.axis.top").call(xAxis_top);
323 | svg.select(".y.axis.right").call(yAxis_right);
324 |
325 | svg.selectAll("circle.FP")
326 | .attr("cx", function (d) { return xScale (d[currentX]); })
327 | .attr("cy", function (d) { return yScale (d[currentY]); })
328 |
329 | svg.selectAll("rect.FP")
330 | .attr("x", function (d) { return xScale (d[currentX]) - symbolsize; })
331 | .attr("y", function (d) { return yScale (d[currentY]) - symbolsize; })
332 |
333 | svg.selectAll("text.FP")
334 | .attr("x", function (d) { return xScale (d[currentX]) - symbolsize/2; })
335 | .attr("y", function (d) { return yScale (d[currentY]) + symbolsize/2; })
336 | }
337 |
338 | //i added this more flexible plotting function to be able to plot different variables on each axis. It takes three optional parameters: the data array, and two axes variables.
339 | function plot(xvar,yvar,data){
340 | //set default values... if plot() is called without arguments, these default values will be used.
341 | xvar = xvar || currentX;
342 | yvar = yvar || currentY;
343 | data = data || FPdata;
344 |
345 | //filter the data according to the user settings for EC, QY, and brightness range
346 | data = data.filter(function(d) { return filtercheck(d) ? d : null; });
347 |
348 | // helper function to iterate through all of the data filters (without having to type them all out)
349 | function filtercheck(data){
350 | for (f in filters){
351 | v = filters[f];
352 | if( data[f] < v[0] || data[f] > v[1] ) {return false;}
353 | }
354 | return true;
355 | }
356 |
357 | //filter out data with empty values
358 | data = data.filter(function(d) {return d[xvar] > 0 && d[yvar] > 0;});
359 |
360 | //update scale domains based on data
361 | xScale.domain([
362 | d3.min (data, function(d) { return .99 * d[xvar]; }),
363 | d3.max (data, function(d) { return 1.01 * d[xvar]; })
364 | ])
365 | .nice();
366 | zoom.x(xScale);
367 |
368 | yScale.domain([
369 | d3.min (data, function(d) { return .99 * d[yvar]; }),
370 | d3.max (data, function(d) { return 1.01 * d[yvar]; })
371 | ])
372 | .nice();
373 | zoom.y(yScale);
374 |
375 | //relabel X and Y axes
376 | svg.select(".x.label").text(strings[xvar])
377 | svg.select(".y.label").text(strings[yvar])
378 |
379 | // Join new data with old elements, if any.
380 | var datagroup = svg.selectAll("g.FP").data(data, function (d){ return d.Name;});
381 | entergroup = datagroup.enter().append("g")
382 | .attr("class", "FP")
383 | .attr("clip-path", "url(#chart-area)")
384 | .call(zoom); //so we can zoom while moused over elements
385 |
386 | entergroup.each(function(d, i) {
387 | //determine type of protein and whether to plot a circle or a square
388 | if (d["type"] =="i"){
389 | //plot new circles
390 | plotcircle(d3.select(this));
391 | } else if (d["type"] =="e"){
392 | // plot new squares
393 | plotsquare(d3.select(this));
394 | }
395 | //add text to markers
396 | plottext(d3.select(this));
397 | })
398 |
399 | // Remove old elements as needed.
400 | datagroup.exit().remove();
401 |
402 | // move circles to their new positions (based on axes) with transition animation
403 | datagroup.each(function(d, i) {
404 | current = d3.select(this)
405 | current.selectAll("circle.FP")
406 | .transition()
407 | .attr("cx", function (d) { return xScale (d[xvar]); })
408 | .attr("cy", function (d) { return yScale (d[yvar]); })
409 | .duration(800); //change this number to speed up or slow down the animation
410 | current.selectAll("rect.FP")
411 | .transition()
412 | .attr("x", function (d) { return xScale (d[xvar]) - symbolsize; })
413 | .attr("y", function (d) { return yScale (d[yvar]) - symbolsize; })
414 | .duration(800); //change this number to speed up or slow down the animation
415 | current.selectAll("text.FP")
416 | .transition()
417 | .attr("x", function (d) { return xScale (d[xvar]) - symbolsize/2; })
418 | .attr("y", function (d) { return yScale (d[yvar]) + symbolsize/2; })
419 | .duration(800); //change this number to speed up or slow down the animation
420 | })
421 |
422 | // these two lines cause the transition animation on the axes... they are also cause chopiness in the user interface when the user slides the range sliders on the right side... uncomment to see their effect.
423 | svg.select(".x.axis.bottom").call(xAxis_bottom);
424 | svg.select(".y.axis.left").call(yAxis_left);
425 | }
426 |
427 | function draw_table() {
428 | columns = Object.keys(tableStrings); //column names
429 | //split up fluorescent proteins by type and add the relevant tables
430 | FPgroups.forEach( function(FPtype) {
431 | function testfilt(element){
432 | return element.lambda_ex >= FPtype.ex_min && element.lambda_ex < FPtype.ex_max
433 | && element.lambda_em >= FPtype.em_min && element.lambda_em < FPtype.em_max;
434 | }
435 |
436 | // var table = d3.select("#table").append("h4")
437 | // .attr("class", "tablename")
438 | // .text(FPtype.Name + " Proteins");
439 | var table = d3.select("#table").append("table");
440 | //add title row
441 | table.append("tr").append("th")
442 | .attr("colspan", columns.length)
443 | .attr("class", "tabletitle")
444 | .style("background-color", FPtype.color)
445 | .text(FPtype.Name + " Proteins");
446 |
447 | tdata = FPdata.filter(testfilt);
448 | table.append("tr")
449 | .attr("class", "header")
450 | .selectAll("th")
451 | .data(columns)
452 | .enter().append("th")
453 | .html(function(d,i) { return tableStrings[columns[i]]; })
454 | .attr("class", function(d,i) { return (d == "Name") ? "col head protein" : "col head numeric"; }); // conditional here to limit the use of unneccesary global variables
455 |
456 | //populate the table
457 | table.selectAll("tr.data")
458 | .data(tdata)
459 | .enter().append("tr")
460 | .attr("class", "data")
461 | .selectAll("td")
462 | .data(function(d) {
463 | return columns.map(function(column, colstyles) {
464 | var sty = (column == "Name") ? "col protein" : "col numeric"; // conditional here removes need for another "styles" table
465 | return {column: column, value: d[column], style: sty};
466 | });
467 | })
468 | .enter().append("td")
469 | .html(function(d) {
470 | if (d.column == "RefNum"){
471 | reflist = d.value.trim().split(" ")
472 | //add links to bibliography
473 | return reflist.reduce(function(out, r){
474 | return out + "[" + r + "] ";
475 | }, "");
476 | //return "" + d.value + "";
477 | }
478 | else{
479 | return d.value;
480 | }
481 | })
482 | .attr("class", function(d) { return d.style; });
483 | }
484 | );
485 | }
486 |
487 |
488 | function doalittledance(int) {
489 | var s = ["QY","E","lambda_em","lambda_ex","brightness"];
490 | setInterval(function() {
491 | var x = s[Math.floor(Math.random() * s.length)];
492 | do{
493 | var y = s[Math.floor(Math.random() * s.length)];
494 | } while (x == y);
495 | plot(x,y);
496 | }, int);
497 |
498 | }
499 |
500 |
501 | //this bit is just a jQuery plugin to make the radio checkboxes on the right side vertical
502 | (function( $ ){
503 | //plugin buttonset vertical
504 | $.fn.buttonsetv = function() {
505 | $(':radio, :checkbox', this).wrap('');
506 | $(this).buttonset();
507 | $('label:first', this).removeClass('ui-corner-left').addClass('ui-corner-top');
508 | $('label:last', this).removeClass('ui-corner-right').addClass('ui-corner-bottom');
509 | mw = 0; // max witdh
510 | $('label', this).each(function(index){
511 | w = $(this).width();
512 | if (w > mw) mw = w;
513 | })
514 | $('label', this).each(function(index){
515 | $(this).width(mw);
516 | })
517 | };
518 | })( jQuery );
--------------------------------------------------------------------------------
/js/psfpvis.js:
--------------------------------------------------------------------------------
1 | //global variables to hold the current variables plotted on each axis
2 | var currentX = "lambda_ex"
3 | var currentY = "lambda_em"
4 | var symbolsize = 7; //radius of circle
5 | var bigsymbolsize = 11; //size to grow to on mouseover
6 | var mouseovertime = 150; //animation timing for mouseovers
7 | //global varable to set the ranges over which the data is filtered.
8 | var filters = {
9 | "lambda_ex" : [350,800,1], // array values represent [min range, max range, step (for the range slider)]
10 | "lambda_em" : [350,800,1],
11 | "E" : [10000,140000,1000],
12 | "QY" : [0,1,0.01],
13 | "brightness": [0,100,1]
14 | }
15 | //string variables for updating the axis labels
16 | var strings = {
17 | "lambda_em" : "Emission Wavelength (nm)",
18 | "lambda_ex" : "Excitation Wavelength (nm)",
19 | "stokes" : "Stokes Shift (nm)",
20 | "E" : "Extinction Coefficient",
21 | "QY" : "Quantum Yield",
22 | "brightness": "Brightness",
23 | "pka" : "pKa",
24 | "bleach" : "Bleaching Half-life (s)",
25 | "mature" : "Maturation Half-time (min)",
26 | "lifetime" : "Lifetime (ns)",
27 | }
28 |
29 | //shorter strings for the table
30 | var tableStrings = {
31 | "Name" : "Protein",
32 | "state" : "State",
33 | "lambda_ex" : "λex (nm)",
34 | "lambda_em" : "λem (nm)",
35 | "E" : "EC",
36 | "QY" : "QY",
37 | "brightness": "Brightness",
38 | "Aggregation" : "Aggregation",
39 | "pka" : "pKa",
40 | "bleach" : "tbleach (s)",
41 | "mature" : "tmature (min)",
42 | "transits" : "Transitions",
43 | "RefNum" : "Ref."
44 | }
45 |
46 | //Protein classes for tables
47 | var FPgroups = [
48 | {"Name" : "Photoactivatible", "type" : "pa", "color" : "#808080"},
49 | {"Name" : "Photoconvertible", "type" : "pc", "color" : "#808080"},
50 | {"Name" : "Photoswitchable", "type" : "ps", "color" : "#808080"}
51 | ]
52 |
53 | //on page load, listen to slider events and respond by updating the filter ranges (and updating the ui)
54 | //this uses jQuery and jQuery UI which have been added to the head of the document.
55 | $(function() {
56 |
57 | //dynamically generate filter sliders based on "filters" object
58 | $.each(filters, function(i,v){
59 | var label = $("").appendTo("#sliders");
60 | var slider = $("").appendTo("#sliders");
61 |
62 | slider.rangeSlider({
63 | bounds:{min: v[0], max: v[1]},
64 | defaultValues:{min: v[0], max: v[1]},
65 | step: v[2],
66 | arrows: false,
67 | formatter:function(val){
68 | return (Math.round(val * 100) / 100);
69 | }
70 | });
71 | });
72 |
73 | // update filter settings when user changes slider
74 | $(".rangeSlider").on("valuesChanging", function(e, data){
75 | var filtID = $(this).attr('id');
76 | filters[filtID][0] = data.values.min;
77 | filters[filtID][1] = data.values.max;
78 | plot();
79 | });
80 |
81 | $("#Xradio").buttonsetv();
82 | $("#Yradio").buttonsetv();
83 |
84 | $( "#Xradio input" ).click(function() {
85 | currentX = $(this).val();
86 | plot();
87 | });
88 | $( "#Yradio input" ).click(function() {
89 | currentY = $(this).val();
90 | plot();
91 | });
92 |
93 | //easter egg
94 | $("#doalittledance").click(function(){doalittledance(1600);});
95 | });
96 |
97 | //load the bibliography
98 | $("#bibliography").load('PSFPs_bibliography.html');
99 |
100 | // Chart dimensions.
101 | var margin = {top: 20, right: 30, bottom: 20, left: 50},
102 | width = 700 - margin.right,
103 | height = 700 - margin.top - margin.bottom;
104 |
105 | //Scales and axes
106 | var xScale = d3.scale.linear()
107 | .range ([0, width]);
108 |
109 | var yScale = d3.scale.linear()
110 | .range ([height, 0]);
111 |
112 | //This scale will set the saturation (gray to saturated color). We will use it for mapping brightness.
113 | var saturationScale = d3.scale.linear()
114 | .range([0, 1])
115 | .domain([0, 100]);
116 |
117 | //This scale will set the hue. We will use it for mapping emission wavelength.
118 | var hueScale = d3.scale.linear()
119 | .range([300, 300, 240, 0, 0])
120 | .domain([200, 405, 440, 650, 850]);
121 |
122 | //X and Y axes
123 | var xAxis_bottom = d3.svg.axis().scale(xScale).tickSize(5).tickSubdivide(true);
124 | var yAxis_left = d3.svg.axis().scale(yScale).tickSize(5).orient("left").tickSubdivide(true);
125 |
126 | //top and right axes are identical but without tick labels
127 | var xAxis_top = d3.svg.axis().scale(xScale).tickSize(5).orient("top").tickSubdivide(true).tickFormat(function (d) { return ''; });;;
128 | var yAxis_right = d3.svg.axis().scale(yScale).tickSize(5).orient("right").tickSubdivide(true).tickFormat(function (d) { return ''; });;
129 |
130 | // Create the SVG container and set the origin.
131 | var svg = d3.select("#graph").append("svg")
132 | .attr("width", width + margin.left + margin.right)
133 | .attr("height", height + margin.top + margin.bottom)
134 | .append("g")
135 | .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
136 |
137 | //Add the axes
138 | svg.append("g")
139 | .attr("class", "x axis bottom")
140 | .attr("transform", "translate(0," + height + ")")
141 | .call(xAxis_bottom);
142 | svg.append("g")
143 | .attr("class", "y axis left")
144 | .call(yAxis_left);
145 | svg.append("g")
146 | .attr("class", "x axis top")
147 | .call(xAxis_top);
148 | svg.append("svg:g")
149 | .attr("class", "y axis right")
150 | .attr("transform", "translate(" + width + ",0)")
151 | .call(yAxis_right);
152 |
153 | // Add an x-axis label.
154 | svg.append("text")
155 | .attr("class", "x label")
156 | .attr("text-anchor", "middle")
157 | .attr("x", width/2 )
158 | .attr("y", height-10)
159 | .text("Excitation wavelength (nm)");
160 |
161 | // Add a y-axis label.
162 | svg.append("text")
163 | .attr("class", "y label")
164 | .attr("text-anchor", "middle")
165 | .attr("x", -height/2)
166 | .attr("y", margin.left-30)
167 | .attr("transform", "rotate(-90)")
168 | .text("Emission wavelength (nm)");
169 |
170 | //Add a clipping path so that data points don't go outside of frame
171 | svg.append("clipPath") //Make a new clipPath
172 | .attr("id", "chart-area") //Assign an ID
173 | .append("rect")
174 | .attr("width", width)
175 | .attr("height", height);
176 |
177 | // Create definition for arrowhead on lines
178 | defs = svg.append("defs"); //where marker definitions will go
179 |
180 | //enable zooming
181 | var zoom = d3.behavior.zoom()
182 | .x(xScale)
183 | .y(yScale)
184 | .scaleExtent([1, 10])
185 | .on("zoom", draw_graph);
186 |
187 | svg.append("rect")
188 | .attr("class", "pane")
189 | .attr("width", width)
190 | .attr("height", height)
191 | .call(zoom);
192 |
193 | //a group to contain the clipping path that all out plots will go into.
194 | plotarea = svg.append("g")
195 | .attr("clip-path", "url(#chart-area)");
196 |
197 | var FPdata = []; //Where the fluorescent protein data table will end up.
198 | var linkdata = []; //links between photoconvertible states
199 |
200 | // load the csv file and plot it
201 | d3.csv("PSFPs_processed.csv", function (data) {
202 | data.forEach(function(d){
203 | d.lambda_em = +d.lambda_em; // typing these variables here for simplicity of code later on
204 | d.lambda_ex = +d.lambda_ex;
205 | d.E = +d.E;
206 | d.QY = +d.QY;
207 | d.brightness = +d.brightness;
208 |
209 | //caclulate Stokes shift
210 | d.stokes = d.lambda_em - d.lambda_ex;
211 |
212 | });
213 |
214 | data.sort(function (a, b) {
215 | return a.lambda_ex - b.lambda_ex;
216 | });
217 |
218 | FPdata = data;
219 |
220 | //Only update max of saturation scale, so that gray corresponds to 0 brightness
221 | //Use 80th percentile as max saturation so that not everything is muddy gray
222 | saturationScale.domain([0,
223 | d3.quantile(FPdata.map(function(a) {return (+a.brightness)}).sort(function(a,b){return a-b}),0.8)
224 | ]);
225 |
226 | d3.csv("links.csv", function (links) {
227 | links.forEach(function(link){
228 | //populate link data with appropriate starting and ending coordinates
229 | link.lambda = +link.lambda;
230 | var startFP = $.grep(FPdata, function(e){ return e.UID == link.state1; });
231 | var endFP = $.grep(FPdata, function(e){ return e.UID == link.state2; });
232 | startFP = startFP[0];
233 | endFP = endFP[0];
234 | link.lambda_ex = [startFP.lambda_ex, endFP.lambda_ex];
235 | link.lambda_em = [startFP.lambda_em, endFP.lambda_em];
236 | link.E = [startFP.E, endFP.E];
237 | link.QY= [startFP.QY, endFP.QY];
238 | link.brightness = [startFP.brightness, endFP.brightness];
239 | link.pka = [startFP.pka, endFP.pka];
240 | link.stokes = [startFP.stokes, endFP.stokes]
241 | link.Name = startFP.Name;
242 | });
243 |
244 | linkdata = links;
245 |
246 | //we have to generate separate markers for each line since markers can't inherit line color
247 | //do this here because we only have to do it once
248 | var markers = defs.selectAll("marker").data(links, function (d){ return d.state1;});
249 | markers.enter().append("marker")
250 | .attr("stroke", function (d) { return d3.hsl(hueScale (d.lambda_sw), 1, 0.5)})
251 | .attr("fill", function (d) { return d3.hsl(hueScale (d.lambda_sw), 1, 0.5)})
252 | .attr("id", function (d){ return "arrowhead" + d.state1;})
253 | .attr("viewBox", "0 -5 10 10")
254 | .attr("refX", 8)
255 | .attr("refY", 0)
256 | .attr("markerUnits", "strokeWidth")
257 | .attr("markerWidth", 5)
258 | .attr("markerHeight", 5)
259 | .attr("orient", "auto")
260 | .append("path")
261 | .attr("d", "M0,-5L10,0L0,5");
262 |
263 | plot();
264 | draw_table();
265 | });
266 | });
267 |
268 | function draw_graph(){
269 | //redraw axes with new domains
270 | svg.select(".x.axis.bottom").call(xAxis_bottom);
271 | svg.select(".y.axis.left").call(yAxis_left);
272 | svg.select(".x.axis.top").call(xAxis_top);
273 | svg.select(".y.axis.right").call(yAxis_right);
274 |
275 | svg.selectAll("circle.PSFP")
276 | .attr("cx", function (d) { return xScale (d[currentX]); })
277 | .attr("cy", function (d) { return yScale (d[currentY]); });
278 |
279 | svg.selectAll("rect.PSFP")
280 | .attr("x", function (d) { return xScale (d[currentX]) - symbolsize; })
281 | .attr("y", function (d) { return yScale (d[currentY]) -symbolsize; });
282 |
283 | svg.selectAll("line.PSFP")
284 | .attr("x1", function (d) { return xScale (d[currentX][0]); })
285 | .attr("x2", function (d) { return xScale (d[currentX][1]); })
286 | .attr("y1", function (d) { return yScale (d[currentY][0]); })
287 | .attr("y2", function (d) { return yScale (d[currentY][1]); });
288 | }
289 |
290 | //i added this more flexible plotting function to be able to plot different variables on each axis. It takes three optional parameters: the data array, and two axes variables.
291 | function plot(xvar,yvar,data,links){
292 | //set default values... if plot() is called without arguments, these default values will be used.
293 | xvar = xvar || currentX;
294 | yvar = yvar || currentY;
295 | data = data || FPdata;
296 | links = links || linkdata;
297 |
298 | //filter the data according to the user settings for EC, QY, and brightness range
299 | //we want to keep proteins where any state satisfies the criteria and only remove proteins where all states fail
300 |
301 | var goodFPs = [];
302 | for (var i=0; i < data.length; i++){
303 | //must pass filtercheck, be non-empty, and not previously recorded.
304 | d = data[i];
305 | if (filtercheck(d) && d[xvar] > 0 && d[yvar] > 0 && goodFPs.indexOf(d.Name) == -1) {
306 | //add Name to keeplist
307 | goodFPs.push(d.Name);
308 | }
309 | }
310 |
311 | // helper function to iterate through all of the data filters (without having to type them all out)
312 | function filtercheck(data){
313 | for (f in filters){
314 | v = filters[f];
315 | if( data[f] < v[0] || data[f] > v[1] ) {return false;}
316 | }
317 | return true;
318 | }
319 | data = data.filter(function(d) { return goodFPs.indexOf(d.Name) > -1; });
320 | links = links.filter(function(d) { return goodFPs.indexOf(d.Name) > -1; });
321 |
322 | //additionally remove off states from data; we don't need to show them. These are found by looking for lambda_ex = 0
323 | data = data.filter(function(d) { return d.lambda_ex > 0; });
324 |
325 | //update scale domains based on data
326 | xScale.domain([
327 | d3.min (data, function(d) { return .99 * d[xvar]; }),
328 | d3.max (data, function(d) { return 1.01 * d[xvar]; })
329 | ])
330 | .nice();
331 | zoom.x(xScale);
332 |
333 | yScale.domain([
334 | d3.min (data, function(d) { return .99 * d[yvar]; }),
335 | d3.max (data, function(d) { return 1.01 * d[yvar]; })
336 | ])
337 | .nice();
338 | zoom.y(yScale);
339 |
340 | //relabel X and Y axes
341 | svg.select(".x.label").text(strings[xvar])
342 | svg.select(".y.label").text(strings[yvar])
343 |
344 | //filter out just photoactivatible proteins, plot them as circles
345 | PAdata = data.filter(function(d) {return d.type == "pa" || d.type =="ps"; });
346 | // Join new data with old elements, if any.
347 | var circle = plotarea.selectAll("circle.PSFP").data(PAdata, function (d){ return d.UID;});
348 |
349 | // Create new elements as needed.
350 | circle.enter().append("circle")
351 | .attr("class", function(d) {
352 | return d.type == "pa" ? "PSFP" : "PSFP ps";})
353 | .attr("r", symbolsize)
354 | .attr("stroke", function (d) { return d3.hsl(hueScale (d.lambda_on), 1, 0.5)})
355 | .style("fill", function (d) { return d3.hsl(hueScale (d.lambda_em), saturationScale (d.brightness), 0.5)})
356 | .on('click', function(e){
357 | if(e.DOI){window.location = "http://dx.doi.org/" + e.DOI;}
358 | })
359 | .on("mouseover", function(d) {
360 | d3.select(this).transition().duration(mouseovertime).attr("r", bigsymbolsize);
361 | draw_tooltip(d, this);})
362 | .on("mouseout", function() {
363 | d3.select(this).transition().duration(mouseovertime).attr("r", symbolsize)
364 | //Hide the tooltip
365 | d3.select("#tooltip").classed("hidden", true);
366 | })
367 | .call(zoom) //so we can zoom while moused over circles as well
368 |
369 | // Remove old elements as needed.
370 | circle.exit().remove();
371 |
372 | // move circles to their new positions (based on axes) with transition animation
373 | circle.transition()
374 | .attr("cx", function (d) { return xScale (d[xvar]); })
375 | .attr("cy", function (d) { return yScale (d[yvar]); })
376 | .duration(800); //change this number to speed up or slow down the animation
377 |
378 | //filter out just photoconvertible proteins, plot them as squares
379 | PCdata = data.filter(function(d) {return d.type == "pc"; });
380 | // Join new data with old elements, if any.
381 | var square = plotarea.selectAll("rect.PSFP").data(PCdata, function (d){ return d.UID;});
382 |
383 |
384 | // Create new elements as needed.
385 | square.enter().append("rect")
386 | .attr("class", "PSFP")
387 | .attr("width", symbolsize*2)
388 | .attr("height", symbolsize*2)
389 | .attr("stroke", "#000")
390 | .style("fill", function (d) { return d3.hsl(hueScale (d.lambda_em), saturationScale (d.brightness), 0.5)})
391 | .on('click', function(e){
392 | if(e.DOI){window.location = "http://dx.doi.org/" + e.DOI;}
393 | })
394 | .on("mouseover", function(d) {
395 | d3.select(this).transition().duration(mouseovertime)
396 | .attr("height", bigsymbolsize*2)
397 | .attr("width", bigsymbolsize*2)
398 | // These lines are supposed to recenter squares so that they grow symmetrically around their center but
399 | // they currently don't work properly; it looks like xvar and yvar are not set properly when changing axes
400 |
401 | // hmmm... xvar and yvar appear to be getting set properly upon changing axis
402 | // confirm with alert(xvar)
403 | // however, when a square enters the DOM in this .append() function it seems to "stick"
404 | // with whatever the current xvar/yvar is at the moment and doesn't change it
405 | // later when the axis are changed. mousing over exposes this outdated databind...
406 | // and also explains why it works fine on whatever axis were set when the given square
407 | // entered the DOM.
408 |
409 | // changing xvar/yvar to currentX/currentY fixes the problem but it's worth leaving comments
410 | // here in case it breaks something later.
411 | // it's possible that creating the plot() function here with optional xvar and yvar arguments
412 | // was a bad idea, it might have been better to just use "global" currentX/currentY for everything.
413 |
414 | .attr("x", function (d) { return xScale (d[currentX]) - bigsymbolsize; })
415 | .attr("y", function (d) { return yScale (d[currentY]) - bigsymbolsize; });
416 | draw_tooltip(d, this);})
417 | .on("mouseout", function() {
418 | d3.select(this).transition().duration(mouseovertime)
419 | .attr("height", symbolsize*2)
420 | .attr("width", symbolsize*2)
421 | .attr("x", function (d) { return xScale (d[currentX]) - symbolsize; })
422 | .attr("y", function (d) { return yScale (d[currentY]) - symbolsize; });
423 | //Hide the tooltip
424 | d3.select("#tooltip").classed("hidden", true);
425 | })
426 | .call(zoom) //so we can zoom while moused over circles as well
427 |
428 | // Remove old elements as needed.
429 | square.exit().remove();
430 |
431 | // move squares to their new positions (based on axes) with transition animation
432 | square.transition()
433 | .attr("x", function (d) { return xScale (d[currentX]) - symbolsize; })
434 | .attr("y", function (d) { return yScale (d[currentY]) - symbolsize; })
435 | .duration(800); //change this number to speed up or slow down the animation
436 |
437 | //Add links for photoconvertible proteins
438 | //remove links that go to proteins in the off state (those with lambda_ex = 0);
439 |
440 | links = links.filter(function(d) { return !d.lambda_ex.some( function(w) { return w == 0}) });
441 |
442 | var line = plotarea.selectAll("line.PSFP").data(links, function (d){ return d.state1;});
443 | line.enter().append("line")
444 | .attr("class", "PSFP")
445 | .attr("stroke", function (d) { return d3.hsl(hueScale (d.lambda_sw), 1, 0.5)})
446 | .attr("marker-end", function (d){ return "url(#arrowhead" + d.state1 +")";})
447 | .call(zoom);
448 |
449 | line.exit().remove();
450 |
451 | line.transition()
452 | .attr("x1", function (d) { return xScale (d[currentX][0]); })
453 | .attr("x2", function (d) { return xScale (d[currentX][1]); })
454 | .attr("y1", function (d) { return yScale (d[currentY][0]); })
455 | .attr("y2", function (d) { return yScale (d[currentY][1]); })
456 | .duration(800); //change this number to speed up or slow down the animation
457 |
458 | // these two lines cause the transition animation on the axes... they are also cause chopiness in the user interface when the user slides the range sliders on the right side... uncomment to see their effect.
459 | svg.select(".x.axis.bottom").call(xAxis_bottom);
460 | svg.select(".y.axis.left").call(yAxis_left);
461 | }
462 |
463 | function draw_tooltip(d, target) {
464 | d3.select(target).text("hi")
465 | //Get target bar's x/y values, then augment for the tooltip
466 | var xPosition = parseFloat(d3.select(target).attr("cx") || d3.select(target).attr("x"))
467 | var yPosition = parseFloat(d3.select(target).attr("cy") || d3.select(target).attr("y"))
468 | if (xPosition520){
474 | yPosition =520;
475 | }
476 | //Update the tooltip position and value
477 | d3.select("#tooltip")
478 | .style("left", xPosition + "px")
479 | .style("top", yPosition + "px")
480 | .select("#exvalue")
481 | .text(d.lambda_ex)
482 | d3.select("#tooltip")
483 | .select("#emvalue")
484 | .text(d.lambda_em);
485 | d3.select("#tooltip")
486 | .select("#ecvalue")
487 | .text(d.E);
488 | d3.select("#tooltip")
489 | .select("#qyvalue")
490 | .text(d.QY);
491 | d3.select("#tooltip")
492 | .select("h3")
493 | .text(d.Name);
494 | d3.select("#tooltip")
495 | .select("#brightnessvalue")
496 | .text(d.brightness);
497 |
498 | //Show the tooltip
499 | d3.select("#tooltip").classed("hidden", false);
500 |
501 | }
502 |
503 | function draw_table() {
504 | columns = Object.keys(tableStrings); //column names
505 | //split up fluorescent proteins by type and add the relevant tables
506 | FPgroups.forEach( function(FPtype) {
507 | function testfilt(element){
508 | return element.type == FPtype.type;
509 | }
510 |
511 | var table = d3.select("#table").append("table");
512 | //add title row
513 | table.append("tr").append("th")
514 | .attr("colspan", columns.length)
515 | .attr("class", "tabletitle")
516 | .style("background-color", FPtype.color)
517 | .text(FPtype.Name + " Proteins");
518 |
519 | //filter table data
520 | //remove proteins with lambda_ex = 0 (off states)
521 | tdata = FPdata.filter(function(d) {return d.lambda_ex > 0;});
522 |
523 | tdata = tdata.filter(testfilt);
524 | table.append("tr")
525 | .attr("class", "header")
526 | .selectAll("th")
527 | .data(columns)
528 | .enter().append("th")
529 | .html(function(d,i) { return tableStrings[columns[i]]; })
530 | .attr("class", function(d,i) { return (d == "Name") ? "col head protein" : "col head numeric"; }); // conditional here to limit the use of unneccesary global variables
531 |
532 | //populate the table
533 | //Can't use d3's data binding because we want to group different states of
534 | //the same molecule as these in different rows of the data table.
535 |
536 | //Get list of all unique proteins from dataset
537 | var FPnames = d3.set(tdata.map(function (d) {return d.Name;}));
538 | FPnames.forEach( function(currprotein) {
539 | //Get all states for current protein
540 | var proteinData = tdata.filter(function(d) {return d.Name == currprotein;});
541 | var nStates = proteinData.length;
542 | for (var i=0; i < nStates; i++){
543 | //Add row
544 | var row = table.append("tr").attr("class", "data");
545 |
546 | if (i == 0){
547 | //First row, add protein name with rowspan
548 | row.append("td")
549 | .attr("class", "col protein")
550 | .attr("rowspan", nStates)
551 | .html(currprotein);
552 | }
553 | //loop over remaining data columns
554 | for (var key in tableStrings) {
555 | if (tableStrings.hasOwnProperty(key) && key != "Name") {
556 | //format text to print in table cell
557 | var text = "";
558 | if (key == "RefNum"){
559 | //add links to bibliography
560 | //make references rowspan to avoid duplication for each state
561 | if (i==0){
562 | row.append("td")
563 | .attr("class", "col numeric")
564 | .attr("rowspan", nStates)
565 | .html("" + proteinData[i][key] + "");
566 | }
567 | }
568 | else if (key == "transits"){
569 | //add transitions table, in a rowspan
570 | if (i==0){
571 | row.append("td")
572 | .attr("class", "col numeric")
573 | .attr("rowspan", nStates)
574 | .html(generate_transitions(currprotein));
575 | }
576 | }
577 | else{
578 | row.append("td")
579 | .attr("class", "col numeric")
580 | .html(proteinData[i][key]);
581 | }
582 |
583 |
584 | }
585 | }
586 | }
587 | });
588 | });
589 | };
590 |
591 | function generate_transitions(name) {
592 | var outputHTML = "";
593 | //get all states for the protein with the given name
594 | var proteinData = FPdata.filter(function(d) {return d.Name == name;})
595 | var statesToProcess = new Array();
596 | var statesProcessed = new Array();
597 |
598 | //get initial state for protein
599 | var startState = proteinData.filter(function(d) {return d.initialState == 1;});
600 | statesToProcess.push(startState[0].UID);
601 | while (statesToProcess.length > 0){
602 | //pull next state off queue
603 | var nextState = statesToProcess.shift();
604 | //check if we've processed it
605 | if (!statesProcessed.some(function(d) {return d == nextState;})) {
606 | var transitions = linkdata.filter(function(d) {return d.state1 == nextState});
607 | outputHTML = print_transitions(transitions, outputHTML);
608 | //add destination states to list to process
609 | transitions.forEach(function (d){statesToProcess.push(d.state2);});
610 | //add current state to processed list
611 | statesProcessed.push(nextState);
612 | }
613 | }
614 | return outputHTML;
615 | }
616 |
617 | function print_transitions(transitions, outputHTML){
618 | transitions.forEach(function(transit) {
619 | //get name of state1
620 | startname = FPdata.filter(function(d) {return d.UID == transit.state1});
621 | startname = startname[0].state;
622 | //get name of state2
623 | endname = FPdata.filter(function(d) {return d.UID == transit.state2});
624 | endname = endname[0].state;
625 | outputHTML = outputHTML + startname + " → " + endname;
626 | outputHTML = outputHTML + ", " + transit.lambda_sw + " nm \n";
627 | return outputHTML;
628 | })
629 | return outputHTML;
630 | }
631 |
632 |
633 | function doalittledance(int) {
634 | var s = ["QY","E","lambda_em","lambda_ex","brightness"];
635 | setInterval(function() {
636 | var x = s[Math.floor(Math.random() * s.length)];
637 | do{
638 | var y = s[Math.floor(Math.random() * s.length)];
639 | } while (x == y);
640 | plot(x,y);
641 | }, int);
642 |
643 | }
644 |
645 |
646 | //this bit is just a jQuery plugin to make the radio checkboxes on the right side vertical
647 | (function( $ ){
648 | //plugin buttonset vertical
649 | $.fn.buttonsetv = function() {
650 | $(':radio, :checkbox', this).wrap('');
651 | $(this).buttonset();
652 | $('label:first', this).removeClass('ui-corner-left').addClass('ui-corner-top');
653 | $('label:last', this).removeClass('ui-corner-right').addClass('ui-corner-bottom');
654 | mw = 0; // max witdh
655 | $('label', this).each(function(index){
656 | w = $(this).width();
657 | if (w > mw) mw = w;
658 | })
659 | $('label', this).each(function(index){
660 | $(this).width(mw);
661 | })
662 | };
663 | })( jQuery );
--------------------------------------------------------------------------------
/js/jQRangeSlider-min.js:
--------------------------------------------------------------------------------
1 | /*! jQRangeSlider 5.3.0 - 2013-07-12 - Copyright (C) Guillaume Gautreau 2012 - MIT and GPLv3 licenses.*/!function(a){"use strict";a.widget("ui.rangeSliderMouseTouch",a.ui.mouse,{enabled:!0,_mouseInit:function(){var b=this;a.ui.mouse.prototype._mouseInit.apply(this),this._mouseDownEvent=!1,this.element.bind("touchstart."+this.widgetName,function(a){return b._touchStart(a)})},_mouseDestroy:function(){a(document).unbind("touchmove."+this.widgetName,this._touchMoveDelegate).unbind("touchend."+this.widgetName,this._touchEndDelegate),a.ui.mouse.prototype._mouseDestroy.apply(this)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},destroy:function(){this._mouseDestroy(),a.ui.mouse.prototype.destroy.apply(this),this._mouseInit=null},_touchStart:function(b){if(!this.enabled)return!1;b.which=1,b.preventDefault(),this._fillTouchEvent(b);var c=this,d=this._mouseDownEvent;this._mouseDown(b),d!==this._mouseDownEvent&&(this._touchEndDelegate=function(a){c._touchEnd(a)},this._touchMoveDelegate=function(a){c._touchMove(a)},a(document).bind("touchmove."+this.widgetName,this._touchMoveDelegate).bind("touchend."+this.widgetName,this._touchEndDelegate))},_mouseDown:function(b){return this.enabled?a.ui.mouse.prototype._mouseDown.apply(this,[b]):!1},_touchEnd:function(b){this._fillTouchEvent(b),this._mouseUp(b),a(document).unbind("touchmove."+this.widgetName,this._touchMoveDelegate).unbind("touchend."+this.widgetName,this._touchEndDelegate),this._mouseDownEvent=!1,a(document).trigger("mouseup")},_touchMove:function(a){return a.preventDefault(),this._fillTouchEvent(a),this._mouseMove(a)},_fillTouchEvent:function(a){var b;b="undefined"==typeof a.targetTouches&&"undefined"==typeof a.changedTouches?a.originalEvent.targetTouches[0]||a.originalEvent.changedTouches[0]:a.targetTouches[0]||a.changedTouches[0],a.pageX=b.pageX,a.pageY=b.pageY}})}(jQuery),function(a){"use strict";a.widget("ui.rangeSliderDraggable",a.ui.rangeSliderMouseTouch,{cache:null,options:{containment:null},_create:function(){a.ui.rangeSliderMouseTouch.prototype._create.apply(this),setTimeout(a.proxy(this._initElementIfNotDestroyed,this),10)},destroy:function(){this.cache=null,a.ui.rangeSliderMouseTouch.prototype.destroy.apply(this)},_initElementIfNotDestroyed:function(){this._mouseInit&&this._initElement()},_initElement:function(){this._mouseInit(),this._cache()},_setOption:function(b,c){"containment"===b&&(this.options.containment=null===c||0===a(c).length?null:a(c))},_mouseStart:function(a){return this._cache(),this.cache.click={left:a.pageX,top:a.pageY},this.cache.initialOffset=this.element.offset(),this._triggerMouseEvent("mousestart"),!0},_mouseDrag:function(a){var b=a.pageX-this.cache.click.left;return b=this._constraintPosition(b+this.cache.initialOffset.left),this._applyPosition(b),this._triggerMouseEvent("sliderDrag"),!1},_mouseStop:function(){this._triggerMouseEvent("stop")},_constraintPosition:function(a){return 0!==this.element.parent().length&&null!==this.cache.parent.offset&&(a=Math.min(a,this.cache.parent.offset.left+this.cache.parent.width-this.cache.width.outer),a=Math.max(a,this.cache.parent.offset.left)),a},_applyPosition:function(a){var b={top:this.cache.offset.top,left:a};this.element.offset({left:a}),this.cache.offset=b},_cacheIfNecessary:function(){null===this.cache&&this._cache()},_cache:function(){this.cache={},this._cacheMargins(),this._cacheParent(),this._cacheDimensions(),this.cache.offset=this.element.offset()},_cacheMargins:function(){this.cache.margin={left:this._parsePixels(this.element,"marginLeft"),right:this._parsePixels(this.element,"marginRight"),top:this._parsePixels(this.element,"marginTop"),bottom:this._parsePixels(this.element,"marginBottom")}},_cacheParent:function(){if(null!==this.options.parent){var a=this.element.parent();this.cache.parent={offset:a.offset(),width:a.width()}}else this.cache.parent=null},_cacheDimensions:function(){this.cache.width={outer:this.element.outerWidth(),inner:this.element.width()}},_parsePixels:function(a,b){return parseInt(a.css(b),10)||0},_triggerMouseEvent:function(a){var b=this._prepareEventData();this.element.trigger(a,b)},_prepareEventData:function(){return{element:this.element,offset:this.cache.offset||null}}})}(jQuery),function(a,b){"use strict";a.widget("ui.rangeSlider",{options:{bounds:{min:0,max:100},defaultValues:{min:20,max:50},wheelMode:null,wheelSpeed:4,arrows:!0,valueLabels:"show",formatter:null,durationIn:0,durationOut:400,delayOut:200,range:{min:!1,max:!1},step:!1,scales:!1,enabled:!0},_values:null,_valuesChanged:!1,bar:null,leftHandle:null,rightHandle:null,innerBar:null,container:null,arrows:null,labels:null,changing:{min:!1,max:!1},changed:{min:!1,max:!1},ruler:null,_create:function(){this._setDefaultValues(),this.labels={left:null,right:null,leftDisplayed:!0,rightDisplayed:!0},this.arrows={left:null,right:null},this.changing={min:!1,max:!1},this.changed={min:!1,max:!1},this._createElements(),this._bindResize(),setTimeout(a.proxy(this.resize,this),1),setTimeout(a.proxy(this._initValues,this),1)},_setDefaultValues:function(){this._values={min:this.options.defaultValues.min,max:this.options.defaultValues.max}},_bindResize:function(){var b=this;this._resizeProxy=function(a){b.resize(a)},a(window).resize(this._resizeProxy)},_initWidth:function(){this.container.css("width",this.element.width()-this.container.outerWidth(!0)+this.container.width()),this.innerBar.css("width",this.container.width()-this.innerBar.outerWidth(!0)+this.innerBar.width())},_initValues:function(){this.values(this._values.min,this._values.max)},_setOption:function(a,b){this._setWheelOption(a,b),this._setArrowsOption(a,b),this._setLabelsOption(a,b),this._setLabelsDurations(a,b),this._setFormatterOption(a,b),this._setBoundsOption(a,b),this._setRangeOption(a,b),this._setStepOption(a,b),this._setScalesOption(a,b),this._setEnabledOption(a,b)},_validProperty:function(a,b,c){return null===a||"undefined"==typeof a[b]?c:a[b]},_setStepOption:function(a,b){"step"===a&&(this.options.step=b,this._leftHandle("option","step",b),this._rightHandle("option","step",b),this._changed(!0))},_setScalesOption:function(a,b){"scales"===a&&(b===!1||null===b?(this.options.scales=!1,this._destroyRuler()):b instanceof Array&&(this.options.scales=b,this._updateRuler()))},_setRangeOption:function(a,b){"range"===a&&(this._bar("option","range",b),this.options.range=this._bar("option","range"),this._changed(!0))},_setBoundsOption:function(a,b){"bounds"===a&&"undefined"!=typeof b.min&&"undefined"!=typeof b.max&&this.bounds(b.min,b.max)},_setWheelOption:function(a,b){("wheelMode"===a||"wheelSpeed"===a)&&(this._bar("option",a,b),this.options[a]=this._bar("option",a))},_setLabelsOption:function(a,b){if("valueLabels"===a){if("hide"!==b&&"show"!==b&&"change"!==b)return;this.options.valueLabels=b,"hide"!==b?(this._createLabels(),this._leftLabel("update"),this._rightLabel("update")):this._destroyLabels()}},_setFormatterOption:function(a,b){"formatter"===a&&null!==b&&"function"==typeof b&&(this.options.formatter=b,"hide"!==this.options.valueLabels&&(this._destroyLabels(),this._createLabels()))},_setArrowsOption:function(a,b){"arrows"!==a||b!==!0&&b!==!1||b===this.options.arrows||(b===!0?(this.element.removeClass("ui-rangeSlider-noArrow").addClass("ui-rangeSlider-withArrows"),this.arrows.left.css("display","block"),this.arrows.right.css("display","block"),this.options.arrows=!0):b===!1&&(this.element.addClass("ui-rangeSlider-noArrow").removeClass("ui-rangeSlider-withArrows"),this.arrows.left.css("display","none"),this.arrows.right.css("display","none"),this.options.arrows=!1),this._initWidth())},_setLabelsDurations:function(a,b){if("durationIn"===a||"durationOut"===a||"delayOut"===a){if(parseInt(b,10)!==b)return;null!==this.labels.left&&this._leftLabel("option",a,b),null!==this.labels.right&&this._rightLabel("option",a,b),this.options[a]=b}},_setEnabledOption:function(a,b){"enabled"===a&&this.toggle(b)},_createElements:function(){"absolute"!==this.element.css("position")&&this.element.css("position","relative"),this.element.addClass("ui-rangeSlider"),this.container=a("").css("position","absolute").appendTo(this.element),this.innerBar=a("").css("position","absolute").css("top",0).css("left",0),this._createHandles(),this._createBar(),this.container.prepend(this.innerBar),this._createArrows(),"hide"!==this.options.valueLabels?this._createLabels():this._destroyLabels(),this._updateRuler(),this.options.enabled||this._toggle(this.options.enabled)},_createHandle:function(b){return a("")[this._handleType()](b).bind("sliderDrag",a.proxy(this._changing,this)).bind("stop",a.proxy(this._changed,this))},_createHandles:function(){this.leftHandle=this._createHandle({isLeft:!0,bounds:this.options.bounds,value:this._values.min,step:this.options.step}).appendTo(this.container),this.rightHandle=this._createHandle({isLeft:!1,bounds:this.options.bounds,value:this._values.max,step:this.options.step}).appendTo(this.container)},_createBar:function(){this.bar=a("").prependTo(this.container).bind("sliderDrag scroll zoom",a.proxy(this._changing,this)).bind("stop",a.proxy(this._changed,this)),this._bar({leftHandle:this.leftHandle,rightHandle:this.rightHandle,values:{min:this._values.min,max:this._values.max},type:this._handleType(),range:this.options.range,wheelMode:this.options.wheelMode,wheelSpeed:this.options.wheelSpeed}),this.options.range=this._bar("option","range"),this.options.wheelMode=this._bar("option","wheelMode"),this.options.wheelSpeed=this._bar("option","wheelSpeed")},_createArrows:function(){this.arrows.left=this._createArrow("left"),this.arrows.right=this._createArrow("right"),this.options.arrows?this.element.addClass("ui-rangeSlider-withArrows"):(this.arrows.left.css("display","none"),this.arrows.right.css("display","none"),this.element.addClass("ui-rangeSlider-noArrow"))},_createArrow:function(b){var c,d=a("").append("").addClass("ui-rangeSlider-"+b+"Arrow").css("position","absolute").css(b,0).appendTo(this.element);return c="right"===b?a.proxy(this._scrollRightClick,this):a.proxy(this._scrollLeftClick,this),d.bind("mousedown touchstart",c),d},_proxy:function(a,b,c){var d=Array.prototype.slice.call(c);return a[b].apply(a,d)},_handleType:function(){return"rangeSliderHandle"},_barType:function(){return"rangeSliderBar"},_bar:function(){return this._proxy(this.bar,this._barType(),arguments)},_labelType:function(){return"rangeSliderLabel"},_leftLabel:function(){return this._proxy(this.labels.left,this._labelType(),arguments)},_rightLabel:function(){return this._proxy(this.labels.right,this._labelType(),arguments)},_leftHandle:function(){return this._proxy(this.leftHandle,this._handleType(),arguments)},_rightHandle:function(){return this._proxy(this.rightHandle,this._handleType(),arguments)},_getValue:function(a,b){return b===this.rightHandle&&(a-=b.outerWidth()),a*(this.options.bounds.max-this.options.bounds.min)/(this.container.innerWidth()-b.outerWidth(!0))+this.options.bounds.min},_trigger:function(a){var b=this;setTimeout(function(){b.element.trigger(a,{label:b.element,values:b.values()})},1)},_changing:function(){this._updateValues()&&(this._trigger("valuesChanging"),this._valuesChanged=!0)},_changed:function(a){(this._updateValues()||this._valuesChanged)&&(this._trigger("valuesChanged"),a!==!0&&this._trigger("userValuesChanged"),this._valuesChanged=!1)},_updateValues:function(){var a=this._leftHandle("value"),b=this._rightHandle("value"),c=this._min(a,b),d=this._max(a,b),e=c!==this._values.min||d!==this._values.max;return this._values.min=this._min(a,b),this._values.max=this._max(a,b),e},_min:function(a,b){return Math.min(a,b)},_max:function(a,b){return Math.max(a,b)},_createLabel:function(b,c){var d;return null===b?(d=this._getLabelConstructorParameters(b,c),b=a("").appendTo(this.element)[this._labelType()](d)):(d=this._getLabelRefreshParameters(b,c),b[this._labelType()](d)),b},_getLabelConstructorParameters:function(a,b){return{handle:b,handleType:this._handleType(),formatter:this._getFormatter(),show:this.options.valueLabels,durationIn:this.options.durationIn,durationOut:this.options.durationOut,delayOut:this.options.delayOut}},_getLabelRefreshParameters:function(){return{formatter:this._getFormatter(),show:this.options.valueLabels,durationIn:this.options.durationIn,durationOut:this.options.durationOut,delayOut:this.options.delayOut}},_getFormatter:function(){return this.options.formatter===!1||null===this.options.formatter?this._defaultFormatter:this.options.formatter},_defaultFormatter:function(a){return Math.round(a)},_destroyLabel:function(a){return null!==a&&(a[this._labelType()]("destroy"),a.remove(),a=null),a},_createLabels:function(){this.labels.left=this._createLabel(this.labels.left,this.leftHandle),this.labels.right=this._createLabel(this.labels.right,this.rightHandle),this._leftLabel("pair",this.labels.right)},_destroyLabels:function(){this.labels.left=this._destroyLabel(this.labels.left),this.labels.right=this._destroyLabel(this.labels.right)},_stepRatio:function(){return this._leftHandle("stepRatio")},_scrollRightClick:function(a){return this.options.enabled?(a.preventDefault(),this._bar("startScroll"),this._bindStopScroll(),this._continueScrolling("scrollRight",4*this._stepRatio(),1),void 0):!1},_continueScrolling:function(a,b,c,d){if(!this.options.enabled)return!1;this._bar(a,c),d=d||5,d--;var e=this,f=16,g=Math.max(1,4/this._stepRatio());this._scrollTimeout=setTimeout(function(){0===d&&(b>f?b=Math.max(f,b/1.5):c=Math.min(g,2*c),d=5),e._continueScrolling(a,b,c,d)},b)},_scrollLeftClick:function(a){return this.options.enabled?(a.preventDefault(),this._bar("startScroll"),this._bindStopScroll(),this._continueScrolling("scrollLeft",4*this._stepRatio(),1),void 0):!1},_bindStopScroll:function(){var b=this;this._stopScrollHandle=function(a){a.preventDefault(),b._stopScroll()},a(document).bind("mouseup touchend",this._stopScrollHandle)},_stopScroll:function(){a(document).unbind("mouseup touchend",this._stopScrollHandle),this._stopScrollHandle=null,this._bar("stopScroll"),clearTimeout(this._scrollTimeout)},_createRuler:function(){this.ruler=a("").appendTo(this.innerBar)},_setRulerParameters:function(){this.ruler.ruler({min:this.options.bounds.min,max:this.options.bounds.max,scales:this.options.scales})},_destroyRuler:function(){null!==this.ruler&&a.fn.ruler&&(this.ruler.ruler("destroy"),this.ruler.remove(),this.ruler=null)},_updateRuler:function(){this._destroyRuler(),this.options.scales!==!1&&a.fn.ruler&&(this._createRuler(),this._setRulerParameters())},values:function(a,b){var c=this._bar("values",a,b);return"undefined"!=typeof a&&"undefined"!=typeof b&&this._changed(!0),c},min:function(a){return this._values.min=this.values(a,this._values.max).min,this._values.min},max:function(a){return this._values.max=this.values(this._values.min,a).max,this._values.max},bounds:function(a,b){return this._isValidValue(a)&&this._isValidValue(b)&&b>a&&(this._setBounds(a,b),this._updateRuler(),this._changed(!0)),this.options.bounds},_isValidValue:function(a){return"undefined"!=typeof a&&parseFloat(a)===a},_setBounds:function(a,b){this.options.bounds={min:a,max:b},this._leftHandle("option","bounds",this.options.bounds),this._rightHandle("option","bounds",this.options.bounds),this._bar("option","bounds",this.options.bounds)},zoomIn:function(a){this._bar("zoomIn",a)},zoomOut:function(a){this._bar("zoomOut",a)},scrollLeft:function(a){this._bar("startScroll"),this._bar("scrollLeft",a),this._bar("stopScroll")},scrollRight:function(a){this._bar("startScroll"),this._bar("scrollRight",a),this._bar("stopScroll")},resize:function(){this._initWidth(),this._leftHandle("update"),this._rightHandle("update"),this._bar("update")},enable:function(){this.toggle(!0)},disable:function(){this.toggle(!1)},toggle:function(a){a===b&&(a=!this.options.enabled),this.options.enabled!==a&&this._toggle(a)},_toggle:function(a){this.options.enabled=a,this.element.toggleClass("ui-rangeSlider-disabled",!a);var b=a?"enable":"disable";this._bar(b),this._leftHandle(b),this._rightHandle(b),this._leftLabel(b),this._rightLabel(b)},destroy:function(){this.element.removeClass("ui-rangeSlider-withArrows ui-rangeSlider-noArrow ui-rangeSlider-disabled"),this._destroyWidgets(),this._destroyElements(),this.element.removeClass("ui-rangeSlider"),this.options=null,a(window).unbind("resize",this._resizeProxy),this._resizeProxy=null,this._bindResize=null,a.Widget.prototype.destroy.apply(this,arguments)},_destroyWidget:function(a){this["_"+a]("destroy"),this[a].remove(),this[a]=null},_destroyWidgets:function(){this._destroyWidget("bar"),this._destroyWidget("leftHandle"),this._destroyWidget("rightHandle"),this._destroyRuler(),this._destroyLabels()},_destroyElements:function(){this.container.remove(),this.container=null,this.innerBar.remove(),this.innerBar=null,this.arrows.left.remove(),this.arrows.right.remove(),this.arrows=null}})}(jQuery),function(a){"use strict";a.widget("ui.rangeSliderHandle",a.ui.rangeSliderDraggable,{currentMove:null,margin:0,parentElement:null,options:{isLeft:!0,bounds:{min:0,max:100},range:!1,value:0,step:!1},_value:0,_left:0,_create:function(){a.ui.rangeSliderDraggable.prototype._create.apply(this),this.element.css("position","absolute").css("top",0).addClass("ui-rangeSlider-handle").toggleClass("ui-rangeSlider-leftHandle",this.options.isLeft).toggleClass("ui-rangeSlider-rightHandle",!this.options.isLeft),this.element.append(""),this._value=this._constraintValue(this.options.value)},destroy:function(){this.element.empty(),a.ui.rangeSliderDraggable.prototype.destroy.apply(this)},_setOption:function(b,c){"isLeft"!==b||c!==!0&&c!==!1||c===this.options.isLeft?"step"===b&&this._checkStep(c)?(this.options.step=c,this.update()):"bounds"===b?(this.options.bounds=c,this.update()):"range"===b&&this._checkRange(c)&&(this.options.range=c,this.update()):(this.options.isLeft=c,this.element.toggleClass("ui-rangeSlider-leftHandle",this.options.isLeft).toggleClass("ui-rangeSlider-rightHandle",!this.options.isLeft),this._position(this._value),this.element.trigger("switch",this.options.isLeft)),a.ui.rangeSliderDraggable.prototype._setOption.apply(this,[b,c])},_checkRange:function(a){return a===!1||!this._isValidValue(a.min)&&!this._isValidValue(a.max)},_isValidValue:function(a){return"undefined"!=typeof a&&a!==!1&&parseFloat(a)!==a},_checkStep:function(a){return a===!1||parseFloat(a)===a},_initElement:function(){a.ui.rangeSliderDraggable.prototype._initElement.apply(this),0===this.cache.parent.width||null===this.cache.parent.width?setTimeout(a.proxy(this._initElementIfNotDestroyed,this),500):(this._position(this._value),this._triggerMouseEvent("initialize"))},_bounds:function(){return this.options.bounds},_cache:function(){a.ui.rangeSliderDraggable.prototype._cache.apply(this),this._cacheParent()},_cacheParent:function(){var a=this.element.parent();this.cache.parent={element:a,offset:a.offset(),padding:{left:this._parsePixels(a,"paddingLeft")},width:a.width()}},_position:function(a){var b=this._getPositionForValue(a);this._applyPosition(b)},_constraintPosition:function(a){var b=this._getValueForPosition(a);return this._getPositionForValue(b)},_applyPosition:function(b){a.ui.rangeSliderDraggable.prototype._applyPosition.apply(this,[b]),this._left=b,this._setValue(this._getValueForPosition(b)),this._triggerMouseEvent("moving")},_prepareEventData:function(){var b=a.ui.rangeSliderDraggable.prototype._prepareEventData.apply(this);return b.value=this._value,b},_setValue:function(a){a!==this._value&&(this._value=a)},_constraintValue:function(a){if(a=Math.min(a,this._bounds().max),a=Math.max(a,this._bounds().min),a=this._round(a),this.options.range!==!1){var b=this.options.range.min||!1,c=this.options.range.max||!1;b!==!1&&(a=Math.max(a,this._round(b))),c!==!1&&(a=Math.min(a,this._round(c))),a=Math.min(a,this._bounds().max),a=Math.max(a,this._bounds().min)}return a},_round:function(a){return this.options.step!==!1&&this.options.step>0?Math.round(a/this.options.step)*this.options.step:a},_getPositionForValue:function(a){if(!this.cache||!this.cache.parent||null===this.cache.parent.offset)return 0;a=this._constraintValue(a);var b=(a-this.options.bounds.min)/(this.options.bounds.max-this.options.bounds.min),c=this.cache.parent.width-this.cache.width.outer,d=this.cache.parent.offset.left;return b*c+d},_getValueForPosition:function(a){var b=this._getRawValueForPositionAndBounds(a,this.options.bounds.min,this.options.bounds.max);return this._constraintValue(b)},_getRawValueForPositionAndBounds:function(a,b,c){var d=null===this.cache.parent.offset?0:this.cache.parent.offset.left,e=this.cache.parent.width-this.cache.width.outer,f=(a-d)/e;return f*(c-b)+b},value:function(a){return"undefined"!=typeof a&&(this._cache(),a=this._constraintValue(a),this._position(a)),this._value},update:function(){this._cache();var a=this._constraintValue(this._value),b=this._getPositionForValue(a);a!==this._value?(this._triggerMouseEvent("updating"),this._position(a),this._triggerMouseEvent("update")):b!==this.cache.offset.left&&(this._triggerMouseEvent("updating"),this._position(a),this._triggerMouseEvent("update"))},position:function(a){return"undefined"!=typeof a&&(this._cache(),a=this._constraintPosition(a),this._applyPosition(a)),this._left},add:function(a,b){return a+b},substract:function(a,b){return a-b},stepsBetween:function(a,b){return this.options.step===!1?b-a:(b-a)/this.options.step},multiplyStep:function(a,b){return a*b},moveRight:function(a){var b;return this.options.step===!1?(b=this._left,this.position(this._left+a),this._left-b):(b=this._value,this.value(this.add(b,this.multiplyStep(this.options.step,a))),this.stepsBetween(b,this._value))},moveLeft:function(a){return-this.moveRight(-a)},stepRatio:function(){if(this.options.step===!1)return 1;var a=(this.options.bounds.max-this.options.bounds.min)/this.options.step;return this.cache.parent.width/a}})}(jQuery),function(a){"use strict";a.widget("ui.rangeSliderBar",a.ui.rangeSliderDraggable,{options:{leftHandle:null,rightHandle:null,bounds:{min:0,max:100},type:"rangeSliderHandle",range:!1,drag:function(){},stop:function(){},values:{min:0,max:20},wheelSpeed:4,wheelMode:null},_values:{min:0,max:20},_waitingToInit:2,_wheelTimeout:!1,_create:function(){a.ui.rangeSliderDraggable.prototype._create.apply(this),this.element.css("position","absolute").css("top",0).addClass("ui-rangeSlider-bar"),this.options.leftHandle.bind("initialize",a.proxy(this._onInitialized,this)).bind("mousestart",a.proxy(this._cache,this)).bind("stop",a.proxy(this._onHandleStop,this)),this.options.rightHandle.bind("initialize",a.proxy(this._onInitialized,this)).bind("mousestart",a.proxy(this._cache,this)).bind("stop",a.proxy(this._onHandleStop,this)),this._bindHandles(),this._values=this.options.values,this._setWheelModeOption(this.options.wheelMode)},destroy:function(){this.options.leftHandle.unbind(".bar"),this.options.rightHandle.unbind(".bar"),this.options=null,a.ui.rangeSliderDraggable.prototype.destroy.apply(this)},_setOption:function(a,b){"range"===a?this._setRangeOption(b):"wheelSpeed"===a?this._setWheelSpeedOption(b):"wheelMode"===a&&this._setWheelModeOption(b)},_setRangeOption:function(a){if(("object"!=typeof a||null===a)&&(a=!1),a!==!1||this.options.range!==!1){if(a!==!1){var b="undefined"==typeof a.min?this.options.range.min||!1:a.min,c="undefined"==typeof a.max?this.options.range.max||!1:a.max;this.options.range={min:b,max:c}}else this.options.range=!1;this._setLeftRange(),this._setRightRange()}},_setWheelSpeedOption:function(a){"number"==typeof a&&a>0&&(this.options.wheelSpeed=a)},_setWheelModeOption:function(a){(null===a||a===!1||"zoom"===a||"scroll"===a)&&(this.options.wheelMode!==a&&this.element.parent().unbind("mousewheel.bar"),this._bindMouseWheel(a),this.options.wheelMode=a)},_bindMouseWheel:function(b){"zoom"===b?this.element.parent().bind("mousewheel.bar",a.proxy(this._mouseWheelZoom,this)):"scroll"===b&&this.element.parent().bind("mousewheel.bar",a.proxy(this._mouseWheelScroll,this))},_setLeftRange:function(){if(this.options.range===!1)return!1;var a=this._values.max,b={min:!1,max:!1};b.max=(this.options.range.min||!1)!==!1?this._leftHandle("substract",a,this.options.range.min):!1,b.min=(this.options.range.max||!1)!==!1?this._leftHandle("substract",a,this.options.range.max):!1,this._leftHandle("option","range",b)},_setRightRange:function(){var a=this._values.min,b={min:!1,max:!1};b.min=(this.options.range.min||!1)!==!1?this._rightHandle("add",a,this.options.range.min):!1,b.max=(this.options.range.max||!1)!==!1?this._rightHandle("add",a,this.options.range.max):!1,this._rightHandle("option","range",b)},_deactivateRange:function(){this._leftHandle("option","range",!1),this._rightHandle("option","range",!1)},_reactivateRange:function(){this._setRangeOption(this.options.range)},_onInitialized:function(){this._waitingToInit--,0===this._waitingToInit&&this._initMe()},_initMe:function(){this._cache(),this.min(this._values.min),this.max(this._values.max);var a=this._leftHandle("position"),b=this._rightHandle("position")+this.options.rightHandle.width();this.element.offset({left:a}),this.element.css("width",b-a)},_leftHandle:function(){return this._handleProxy(this.options.leftHandle,arguments)},_rightHandle:function(){return this._handleProxy(this.options.rightHandle,arguments)},_handleProxy:function(a,b){var c=Array.prototype.slice.call(b);return a[this.options.type].apply(a,c)},_cache:function(){a.ui.rangeSliderDraggable.prototype._cache.apply(this),this._cacheHandles()},_cacheHandles:function(){this.cache.rightHandle={},this.cache.rightHandle.width=this.options.rightHandle.width(),this.cache.rightHandle.offset=this.options.rightHandle.offset(),this.cache.leftHandle={},this.cache.leftHandle.offset=this.options.leftHandle.offset()},_mouseStart:function(b){a.ui.rangeSliderDraggable.prototype._mouseStart.apply(this,[b]),this._deactivateRange()},_mouseStop:function(b){a.ui.rangeSliderDraggable.prototype._mouseStop.apply(this,[b]),this._cacheHandles(),this._values.min=this._leftHandle("value"),this._values.max=this._rightHandle("value"),this._reactivateRange(),this._leftHandle().trigger("stop"),this._rightHandle().trigger("stop")},_onDragLeftHandle:function(a,b){return this._cacheIfNecessary(),this._switchedValues()?(this._switchHandles(),this._onDragRightHandle(a,b),void 0):(this._values.min=b.value,this.cache.offset.left=b.offset.left,this.cache.leftHandle.offset=b.offset,this._positionBar(),void 0)},_onDragRightHandle:function(a,b){return this._cacheIfNecessary(),this._switchedValues()?(this._switchHandles(),this._onDragLeftHandle(a,b),void 0):(this._values.max=b.value,this.cache.rightHandle.offset=b.offset,this._positionBar(),void 0)},_positionBar:function(){var a=this.cache.rightHandle.offset.left+this.cache.rightHandle.width-this.cache.leftHandle.offset.left;this.cache.width.inner=a,this.element.css("width",a).offset({left:this.cache.leftHandle.offset.left})},_onHandleStop:function(){this._setLeftRange(),this._setRightRange()},_switchedValues:function(){if(this.min()>this.max()){var a=this._values.min;return this._values.min=this._values.max,this._values.max=a,!0}return!1},_switchHandles:function(){var a=this.options.leftHandle;this.options.leftHandle=this.options.rightHandle,this.options.rightHandle=a,this._leftHandle("option","isLeft",!0),this._rightHandle("option","isLeft",!1),this._bindHandles(),this._cacheHandles()},_bindHandles:function(){this.options.leftHandle.unbind(".bar").bind("sliderDrag.bar update.bar moving.bar",a.proxy(this._onDragLeftHandle,this)),this.options.rightHandle.unbind(".bar").bind("sliderDrag.bar update.bar moving.bar",a.proxy(this._onDragRightHandle,this))},_constraintPosition:function(b){var c,d={};return d.left=a.ui.rangeSliderDraggable.prototype._constraintPosition.apply(this,[b]),d.left=this._leftHandle("position",d.left),c=this._rightHandle("position",d.left+this.cache.width.outer-this.cache.rightHandle.width),d.width=c-d.left+this.cache.rightHandle.width,d},_applyPosition:function(b){a.ui.rangeSliderDraggable.prototype._applyPosition.apply(this,[b.left]),this.element.width(b.width)},_mouseWheelZoom:function(b,c,d,e){var f=this._values.min+(this._values.max-this._values.min)/2,g={},h={};return this.options.range===!1||this.options.range.min===!1?(g.max=f,h.min=f):(g.max=f-this.options.range.min/2,h.min=f+this.options.range.min/2),this.options.range!==!1&&this.options.range.max!==!1&&(g.min=f-this.options.range.max/2,h.max=f+this.options.range.max/2),this._leftHandle("option","range",g),this._rightHandle("option","range",h),clearTimeout(this._wheelTimeout),this._wheelTimeout=setTimeout(a.proxy(this._wheelStop,this),200),this.zoomOut(e*this.options.wheelSpeed),!1},_mouseWheelScroll:function(b,c,d,e){return this._wheelTimeout===!1?this.startScroll():clearTimeout(this._wheelTimeout),this._wheelTimeout=setTimeout(a.proxy(this._wheelStop,this),200),this.scrollLeft(e*this.options.wheelSpeed),!1},_wheelStop:function(){this.stopScroll(),this._wheelTimeout=!1},min:function(a){return this._leftHandle("value",a)},max:function(a){return this._rightHandle("value",a)},startScroll:function(){this._deactivateRange()},stopScroll:function(){this._reactivateRange(),this._triggerMouseEvent("stop"),this._leftHandle().trigger("stop"),this._rightHandle().trigger("stop")},scrollLeft:function(a){return a=a||1,0>a?this.scrollRight(-a):(a=this._leftHandle("moveLeft",a),this._rightHandle("moveLeft",a),this.update(),this._triggerMouseEvent("scroll"),void 0)},scrollRight:function(a){return a=a||1,0>a?this.scrollLeft(-a):(a=this._rightHandle("moveRight",a),this._leftHandle("moveRight",a),this.update(),this._triggerMouseEvent("scroll"),void 0)},zoomIn:function(a){if(a=a||1,0>a)return this.zoomOut(-a);var b=this._rightHandle("moveLeft",a);a>b&&(b/=2,this._rightHandle("moveRight",b)),this._leftHandle("moveRight",b),this.update(),this._triggerMouseEvent("zoom")},zoomOut:function(a){if(a=a||1,0>a)return this.zoomIn(-a);var b=this._rightHandle("moveRight",a);a>b&&(b/=2,this._rightHandle("moveLeft",b)),this._leftHandle("moveLeft",b),this.update(),this._triggerMouseEvent("zoom")},values:function(a,b){if("undefined"!=typeof a&&"undefined"!=typeof b){var c=Math.min(a,b),d=Math.max(a,b);this._deactivateRange(),this.options.leftHandle.unbind(".bar"),this.options.rightHandle.unbind(".bar"),this._values.min=this._leftHandle("value",c),this._values.max=this._rightHandle("value",d),this._bindHandles(),this._reactivateRange(),this.update()}return{min:this._values.min,max:this._values.max}},update:function(){this._values.min=this.min(),this._values.max=this.max(),this._cache(),this._positionBar()}})}(jQuery),function(a){"use strict";function b(b,c,d,e){this.label1=b,this.label2=c,this.type=d,this.options=e,this.handle1=this.label1[this.type]("option","handle"),this.handle2=this.label2[this.type]("option","handle"),this.cache=null,this.left=b,this.right=c,this.moving=!1,this.initialized=!1,this.updating=!1,this.Init=function(){this.BindHandle(this.handle1),this.BindHandle(this.handle2),"show"===this.options.show?(setTimeout(a.proxy(this.PositionLabels,this),1),this.initialized=!0):setTimeout(a.proxy(this.AfterInit,this),1e3),this._resizeProxy=a.proxy(this.onWindowResize,this),a(window).resize(this._resizeProxy)},this.Destroy=function(){this._resizeProxy&&(a(window).unbind("resize",this._resizeProxy),this._resizeProxy=null,this.handle1.unbind(".positionner"),this.handle1=null,this.handle2.unbind(".positionner"),this.handle2=null,this.label1=null,this.label2=null,this.left=null,this.right=null),this.cache=null},this.AfterInit=function(){this.initialized=!0},this.Cache=function(){"none"!==this.label1.css("display")&&(this.cache={},this.cache.label1={},this.cache.label2={},this.cache.handle1={},this.cache.handle2={},this.cache.offsetParent={},this.CacheElement(this.label1,this.cache.label1),this.CacheElement(this.label2,this.cache.label2),this.CacheElement(this.handle1,this.cache.handle1),this.CacheElement(this.handle2,this.cache.handle2),this.CacheElement(this.label1.offsetParent(),this.cache.offsetParent))
2 | },this.CacheIfNecessary=function(){null===this.cache?this.Cache():(this.CacheWidth(this.label1,this.cache.label1),this.CacheWidth(this.label2,this.cache.label2),this.CacheHeight(this.label1,this.cache.label1),this.CacheHeight(this.label2,this.cache.label2),this.CacheWidth(this.label1.offsetParent(),this.cache.offsetParent))},this.CacheElement=function(a,b){this.CacheWidth(a,b),this.CacheHeight(a,b),b.offset=a.offset(),b.margin={left:this.ParsePixels("marginLeft",a),right:this.ParsePixels("marginRight",a)},b.border={left:this.ParsePixels("borderLeftWidth",a),right:this.ParsePixels("borderRightWidth",a)}},this.CacheWidth=function(a,b){b.width=a.width(),b.outerWidth=a.outerWidth()},this.CacheHeight=function(a,b){b.outerHeightMargin=a.outerHeight(!0)},this.ParsePixels=function(a,b){return parseInt(b.css(a),10)||0},this.BindHandle=function(b){b.bind("updating.positionner",a.proxy(this.onHandleUpdating,this)),b.bind("update.positionner",a.proxy(this.onHandleUpdated,this)),b.bind("moving.positionner",a.proxy(this.onHandleMoving,this)),b.bind("stop.positionner",a.proxy(this.onHandleStop,this))},this.PositionLabels=function(){if(this.CacheIfNecessary(),null!==this.cache){var a=this.GetRawPosition(this.cache.label1,this.cache.handle1),b=this.GetRawPosition(this.cache.label2,this.cache.handle2);this.ConstraintPositions(a,b),this.PositionLabel(this.label1,a.left,this.cache.label1),this.PositionLabel(this.label2,b.left,this.cache.label2)}},this.PositionLabel=function(a,b,c){var d,e,f,g=this.cache.offsetParent.offset.left+this.cache.offsetParent.border.left;g-b>=0?(a.css("right",""),a.offset({left:b})):(d=g+this.cache.offsetParent.width,e=b+c.margin.left+c.outerWidth+c.margin.right,f=d-e,a.css("left",""),a.css("right",f))},this.ConstraintPositions=function(a,b){a.centerb.outerLeft?(a=this.getLeftPosition(a,b),b=this.getRightPosition(a,b)):a.center>b.center&&b.outerRight>a.outerLeft&&(b=this.getLeftPosition(b,a),a=this.getRightPosition(b,a))},this.getLeftPosition=function(a,b){var c=(b.center+a.center)/2,d=c-a.cache.outerWidth-a.cache.margin.right+a.cache.border.left;return a.left=d,a},this.getRightPosition=function(a,b){var c=(b.center+a.center)/2;return b.left=c+b.cache.margin.left+b.cache.border.left,b},this.ShowIfNecessary=function(){"show"===this.options.show||this.moving||!this.initialized||this.updating||(this.label1.stop(!0,!0).fadeIn(this.options.durationIn||0),this.label2.stop(!0,!0).fadeIn(this.options.durationIn||0),this.moving=!0)},this.HideIfNeeded=function(){this.moving===!0&&(this.label1.stop(!0,!0).delay(this.options.delayOut||0).fadeOut(this.options.durationOut||0),this.label2.stop(!0,!0).delay(this.options.delayOut||0).fadeOut(this.options.durationOut||0),this.moving=!1)},this.onHandleMoving=function(a,b){this.ShowIfNecessary(),this.CacheIfNecessary(),this.UpdateHandlePosition(b),this.PositionLabels()},this.onHandleUpdating=function(){this.updating=!0},this.onHandleUpdated=function(){this.updating=!1,this.cache=null},this.onHandleStop=function(){this.HideIfNeeded()},this.onWindowResize=function(){this.cache=null},this.UpdateHandlePosition=function(a){null!==this.cache&&(a.element[0]===this.handle1[0]?this.UpdatePosition(a,this.cache.handle1):this.UpdatePosition(a,this.cache.handle2))},this.UpdatePosition=function(a,b){b.offset=a.offset},this.GetRawPosition=function(a,b){var c=b.offset.left+b.outerWidth/2,d=c-a.outerWidth/2,e=d+a.outerWidth-a.border.left-a.border.right,f=d-a.margin.left-a.border.left,g=b.offset.top-a.outerHeightMargin;return{left:d,outerLeft:f,top:g,right:e,outerRight:f+a.outerWidth+a.margin.left+a.margin.right,cache:a,center:c}},this.Init()}a.widget("ui.rangeSliderLabel",a.ui.rangeSliderMouseTouch,{options:{handle:null,formatter:!1,handleType:"rangeSliderHandle",show:"show",durationIn:0,durationOut:500,delayOut:500,isLeft:!1},cache:null,_positionner:null,_valueContainer:null,_innerElement:null,_create:function(){this.options.isLeft=this._handle("option","isLeft"),this.element.addClass("ui-rangeSlider-label").css("position","absolute").css("display","block"),this._createElements(),this._toggleClass(),this.options.handle.bind("moving.label",a.proxy(this._onMoving,this)).bind("update.label",a.proxy(this._onUpdate,this)).bind("switch.label",a.proxy(this._onSwitch,this)),"show"!==this.options.show&&this.element.hide(),this._mouseInit()},destroy:function(){this.options.handle.unbind(".label"),this.options.handle=null,this._valueContainer=null,this._innerElement=null,this.element.empty(),this._positionner&&(this._positionner.Destroy(),this._positionner=null),a.ui.rangeSliderMouseTouch.prototype.destroy.apply(this)},_createElements:function(){this._valueContainer=a("").appendTo(this.element),this._innerElement=a("").appendTo(this.element)},_handle:function(){var a=Array.prototype.slice.apply(arguments);return this.options.handle[this.options.handleType].apply(this.options.handle,a)},_setOption:function(a,b){"show"===a?this._updateShowOption(b):("durationIn"===a||"durationOut"===a||"delayOut"===a)&&this._updateDurations(a,b)},_updateShowOption:function(a){this.options.show=a,"show"!==this.options.show?this.element.hide():(this.element.show(),this._display(this.options.handle[this.options.handleType]("value")),this._positionner.PositionLabels()),this._positionner.options.show=this.options.show},_updateDurations:function(a,b){parseInt(b,10)===b&&(this._positionner.options[a]=b,this.options[a]=b)},_display:function(a){this.options.formatter===!1?this._displayText(Math.round(a)):this._displayText(this.options.formatter(a))},_displayText:function(a){this._valueContainer.text(a)},_toggleClass:function(){this.element.toggleClass("ui-rangeSlider-leftLabel",this.options.isLeft).toggleClass("ui-rangeSlider-rightLabel",!this.options.isLeft)},_mouseDown:function(a){this.options.handle.trigger(a)},_mouseUp:function(a){this.options.handle.trigger(a)},_mouseMove:function(a){this.options.handle.trigger(a)},_onMoving:function(a,b){this._display(b.value)},_onUpdate:function(){"show"===this.options.show&&this.update()},_onSwitch:function(a,b){this.options.isLeft=b,this._toggleClass(),this._positionner.PositionLabels()},pair:function(a){null===this._positionner&&(this._positionner=new b(this.element,a,this.widgetName,{show:this.options.show,durationIn:this.options.durationIn,durationOut:this.options.durationOut,delayOut:this.options.delayOut}),a[this.widgetName]("positionner",this._positionner))},positionner:function(a){return"undefined"!=typeof a&&(this._positionner=a),this._positionner},update:function(){this._positionner.cache=null,this._display(this._handle("value")),"show"===this.options.show&&this._positionner.PositionLabels()}})}(jQuery);
--------------------------------------------------------------------------------