├── README.md ├── presentacion_curso_2014 ├── images │ ├── scipy.jpg │ ├── sympy.jpg │ ├── cilindro.png │ ├── equation.PNG │ ├── kepler.png │ ├── perfil.png │ ├── IPy_header.png │ ├── ejercicio.png │ ├── numpylogo.png │ ├── preguntas.png │ ├── UPM-Rectorado--0.jpg │ ├── Ingeniero_Aeronautico.png │ └── python-logo-master-v3-TM.png ├── custom.css ├── reveal.js │ ├── lib │ │ ├── css │ │ │ └── zenburn.css │ │ └── js │ │ │ └── head.min.js │ ├── js │ │ ├── head.min.js │ │ └── reveal.js │ └── css │ │ ├── theme │ │ └── simple.css │ │ └── reveal.css └── styles │ └── numericalmoocstyle.css ├── 14.10.2014-Charla_1 ├── WxPython │ ├── Breve introducción a las interfaces gráficas con wxPython.pdf │ └── Ejemplos │ │ ├── Flujo calor │ │ ├── app.py │ │ ├── MplPanel_1.py │ │ ├── MplPanel_2.py │ │ └── Cuadro.py │ │ ├── Sumas │ │ └── Generador aleatorio └── Animaciones_matplotlib-Albertp_Lorenzo.ipynb ├── .gitattributes └── .gitignore /README.md: -------------------------------------------------------------------------------- 1 | Charlas_AeroPython 2 | ================== 3 | 4 | Notebooks y otro material utilizado en las charlas de AeroPython ETSIAE (UPM) 5 | -------------------------------------------------------------------------------- /presentacion_curso_2014/images/scipy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Charlas_AeroPython/master/presentacion_curso_2014/images/scipy.jpg -------------------------------------------------------------------------------- /presentacion_curso_2014/images/sympy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Charlas_AeroPython/master/presentacion_curso_2014/images/sympy.jpg -------------------------------------------------------------------------------- /presentacion_curso_2014/images/cilindro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Charlas_AeroPython/master/presentacion_curso_2014/images/cilindro.png -------------------------------------------------------------------------------- /presentacion_curso_2014/images/equation.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Charlas_AeroPython/master/presentacion_curso_2014/images/equation.PNG -------------------------------------------------------------------------------- /presentacion_curso_2014/images/kepler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Charlas_AeroPython/master/presentacion_curso_2014/images/kepler.png -------------------------------------------------------------------------------- /presentacion_curso_2014/images/perfil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Charlas_AeroPython/master/presentacion_curso_2014/images/perfil.png -------------------------------------------------------------------------------- /presentacion_curso_2014/images/IPy_header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Charlas_AeroPython/master/presentacion_curso_2014/images/IPy_header.png -------------------------------------------------------------------------------- /presentacion_curso_2014/images/ejercicio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Charlas_AeroPython/master/presentacion_curso_2014/images/ejercicio.png -------------------------------------------------------------------------------- /presentacion_curso_2014/images/numpylogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Charlas_AeroPython/master/presentacion_curso_2014/images/numpylogo.png -------------------------------------------------------------------------------- /presentacion_curso_2014/images/preguntas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Charlas_AeroPython/master/presentacion_curso_2014/images/preguntas.png -------------------------------------------------------------------------------- /presentacion_curso_2014/images/UPM-Rectorado--0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Charlas_AeroPython/master/presentacion_curso_2014/images/UPM-Rectorado--0.jpg -------------------------------------------------------------------------------- /presentacion_curso_2014/images/Ingeniero_Aeronautico.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Charlas_AeroPython/master/presentacion_curso_2014/images/Ingeniero_Aeronautico.png -------------------------------------------------------------------------------- /presentacion_curso_2014/images/python-logo-master-v3-TM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Charlas_AeroPython/master/presentacion_curso_2014/images/python-logo-master-v3-TM.png -------------------------------------------------------------------------------- /14.10.2014-Charla_1/WxPython/Breve introducción a las interfaces gráficas con wxPython.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Charlas_AeroPython/master/14.10.2014-Charla_1/WxPython/Breve introducción a las interfaces gráficas con wxPython.pdf -------------------------------------------------------------------------------- /14.10.2014-Charla_1/WxPython/Ejemplos/Flujo calor/app.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # generated by wxGlade 0.6.4 on Wed Apr 9 13:14:56 2014 4 | 5 | import wx 6 | from Cuadro import Cuadro 7 | 8 | if __name__ == "__main__": 9 | app = wx.PySimpleApp(0) 10 | wx.InitAllImageHandlers() 11 | frame_1 = Cuadro(None, -1, "") 12 | app.SetTopWindow(frame_1) 13 | frame_1.Show() 14 | app.MainLoop() 15 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /14.10.2014-Charla_1/WxPython/Ejemplos/Flujo calor/MplPanel_1.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # generated by wxGlade 0.6.4 on Wed Apr 9 13:14:56 2014 3 | 4 | import wx 5 | from matplotlib.figure import Figure 6 | from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas 7 | #from numpy import * 8 | 9 | #import matplotlib.pyplot as plt 10 | #from matplotlib import cm 11 | #from mpl_toolkits.mplot3d import Axes3D 12 | 13 | # begin wxGlade: dependencies 14 | # end wxGlade 15 | 16 | # begin wxGlade: extracode 17 | 18 | # end wxGlade 19 | 20 | 21 | class MplPanel_1(wx.Panel): 22 | def __init__(self, *args, **kwds): 23 | # begin wxGlade: MplPanel_1.__init__ 24 | kwds["style"] = wx.TAB_TRAVERSAL 25 | wx.Panel.__init__(self, *args, **kwds) 26 | 27 | self.__set_properties() 28 | self.__do_layout() 29 | # end wxGlade 30 | 31 | ########################## 32 | self.figure = Figure(figsize=(6, 6), dpi=100) 33 | self.canvas = FigureCanvas(self, wx.ID_ANY, self.figure) 34 | ########################## 35 | 36 | 37 | 38 | def __set_properties(self): 39 | # begin wxGlade: MplPanel_1.__set_properties 40 | pass 41 | # end wxGlade 42 | 43 | def __do_layout(self): 44 | # begin wxGlade: MplPanel_1.__do_layout 45 | pass 46 | # end wxGlade 47 | 48 | # end of class MplPanel_1 49 | -------------------------------------------------------------------------------- /14.10.2014-Charla_1/WxPython/Ejemplos/Flujo calor/MplPanel_2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # generated by wxGlade 0.6.4 on Wed Apr 9 13:14:56 2014 3 | 4 | import wx 5 | from matplotlib.figure import Figure 6 | from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas 7 | #import matplotlib.pyplot as plt 8 | #from numpy import * 9 | 10 | #import matplotlib.pyplot as plt 11 | #from matplotlib import cm 12 | #from mpl_toolkits.mplot3d import Axes3D 13 | # begin wxGlade: dependencies 14 | # end wxGlade 15 | 16 | # begin wxGlade: extracode 17 | 18 | # end wxGlade 19 | 20 | 21 | class MplPanel_2(wx.Panel): 22 | def __init__(self, *args, **kwds): 23 | # begin wxGlade: MplPanel_2.__init__ 24 | kwds["style"] = wx.TAB_TRAVERSAL 25 | wx.Panel.__init__(self, *args, **kwds) 26 | 27 | self.__set_properties() 28 | self.__do_layout() 29 | # end wxGlade 30 | ######################## 31 | self.figure = Figure(figsize=(6, 6), dpi=100) 32 | self.canvas = FigureCanvas(self, wx.ID_ANY, self.figure) 33 | ######################## 34 | 35 | 36 | def __set_properties(self): 37 | # begin wxGlade: MplPanel_2.__set_properties 38 | pass 39 | # end wxGlade 40 | 41 | def __do_layout(self): 42 | # begin wxGlade: MplPanel_2.__do_layout 43 | pass 44 | # end wxGlade 45 | 46 | # end of class MplPanel_2 47 | -------------------------------------------------------------------------------- /presentacion_curso_2014/custom.css: -------------------------------------------------------------------------------- 1 | Reveal.initialize({ 2 | 3 | ... 4 | 5 | // The "normal" size of the presentation, aspect ratio will be preserved 6 | // when the presentation is scaled to fit different resolutions. Can be 7 | // specified using percentage units. 8 | width: 960, 9 | height: 700, 10 | 11 | // Factor of the display size that should remain empty around the content 12 | margin: 0.1, 13 | 14 | // Bounds for smallest/largest possible scale to apply to content 15 | minScale: 0.2, 16 | maxScale: 1.0 17 | 18 | }); 19 | lides 20 | center: true, 21 | 22 | // Enables touch navigation on devices with touch input 23 | touch: true, 24 | 25 | // Loop the presentation 26 | loop: false, 27 | 28 | // Change the presentation direction to be RTL 29 | rtl: false, 30 | 31 | // Turns fragments on and off globally 32 | fragments: true, 33 | 34 | // Flags if the presentation is running in an embedded mode, 35 | // i.e. contained within a limited portion of the screen 36 | embedded: false, 37 | 38 | // Number of milliseconds between automatically proceeding to the 39 | // next slide, disabled when set to 0, this value can be overwritten 40 | // by using a data-autoslide attribute on your slides 41 | autoSlide: 0, 42 | 43 | // Stop auto-sliding after user input 44 | autoSlideStoppable: true, 45 | 46 | // Enable slide navigation via mouse wheel 47 | mouseWheel: false, 48 | 49 | // Hides the address bar on mobile devices 50 | hideAddressBar: true, 51 | 52 | // Opens links in an iframe preview overlay 53 | previewLinks: false, 54 | 55 | // Transition style 56 | transition: 'default', // default/cube/page/concave/zoom/linear/fade/none 57 | 58 | // Transition speed 59 | transitionSpeed: 'default', // default/fast/slow 60 | 61 | // Transition style for full page slide backgrounds 62 | backgroundTransition: 'default', // default/none/slide/concave/convex/zoom 63 | 64 | // Number of slides away from the current that are visible 65 | viewDistance: 3, 66 | 67 | // Parallax background image 68 | parallaxBackgroundImage: '', // e.g. "'https://s3.amazonaws.com/hakim-static/reveal-js/reveal-parallax-1.jpg'" 69 | 70 | // Parallax background size 71 | parallaxBackgroundSize: '' // CSS syntax, e.g. "2100px 900px" 72 | 73 | 74 | }); 75 | -------------------------------------------------------------------------------- /14.10.2014-Charla_1/WxPython/Ejemplos/Sumas: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # generated by wxGlade 0.6.4 on Thu Apr 10 10:20:08 2014 4 | 5 | import wx 6 | 7 | # begin wxGlade: extracode 8 | # end wxGlade 9 | 10 | 11 | class Cuadro(wx.Frame): 12 | def __init__(self, *args, **kwds): 13 | # begin wxGlade: Cuadro.__init__ 14 | kwds["style"] = wx.DEFAULT_FRAME_STYLE 15 | wx.Frame.__init__(self, *args, **kwds) 16 | self.Sumando1 = wx.TextCtrl(self, -1, "") 17 | self.label_1 = wx.StaticText(self, -1, "+", style=wx.ALIGN_CENTRE) 18 | self.Sumando2 = wx.TextCtrl(self, -1, "") 19 | self.label_2 = wx.StaticText(self, -1, "=") 20 | self.text_ctrl_3 = wx.TextCtrl(self, -1, "") 21 | self.Sumar = wx.Button(self, -1, "Sumar") 22 | 23 | self.__set_properties() 24 | self.__do_layout() 25 | 26 | self.Bind(wx.EVT_BUTTON, self.onclick, self.Sumar) 27 | # end wxGlade 28 | 29 | def __set_properties(self): 30 | # begin wxGlade: Cuadro.__set_properties 31 | self.SetTitle("Programa de sumar") 32 | # end wxGlade 33 | 34 | def __do_layout(self): 35 | # begin wxGlade: Cuadro.__do_layout 36 | sizer_1 = wx.BoxSizer(wx.VERTICAL) 37 | sizer_2 = wx.BoxSizer(wx.HORIZONTAL) 38 | sizer_2.Add(self.Sumando1, 0, 0, 0) 39 | sizer_2.Add(self.label_1, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL, 0) 40 | sizer_2.Add(self.Sumando2, 0, 0, 0) 41 | sizer_2.Add(self.label_2, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL, 0) 42 | sizer_2.Add(self.text_ctrl_3, 0, 0, 0) 43 | sizer_2.Add(self.Sumar, 0, 0, 0) 44 | sizer_1.Add(sizer_2, 1, wx.EXPAND, 0) 45 | self.SetSizer(sizer_1) 46 | sizer_1.Fit(self) 47 | self.Layout() 48 | # end wxGlade 49 | 50 | def onclick(self, event): # wxGlade: Cuadro. 51 | 52 | a = float(self.Sumando1.GetValue()) 53 | b = float(self.Sumando2.GetValue()) 54 | c = a + b 55 | c = str(c) 56 | self.text_ctrl_3.SetValue(c) 57 | event.Skip() 58 | 59 | # end of class Cuadro 60 | if __name__ == "__main__": 61 | app = wx.PySimpleApp(0) 62 | wx.InitAllImageHandlers() 63 | frame_1 = Cuadro(None, -1, "") 64 | app.SetTopWindow(frame_1) 65 | frame_1.Show() 66 | app.MainLoop() 67 | -------------------------------------------------------------------------------- /presentacion_curso_2014/reveal.js/lib/css/zenburn.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Zenburn style from voldmar.ru (c) Vladimir Epifanov 4 | based on dark.css by Ivan Sagalaev 5 | 6 | */ 7 | 8 | pre code { 9 | display: block; padding: 0.5em; 10 | background: #3F3F3F; 11 | color: #DCDCDC; 12 | } 13 | 14 | pre .keyword, 15 | pre .tag, 16 | pre .django .tag, 17 | pre .django .keyword, 18 | pre .css .class, 19 | pre .css .id, 20 | pre .lisp .title { 21 | color: #E3CEAB; 22 | } 23 | 24 | pre .django .template_tag, 25 | pre .django .variable, 26 | pre .django .filter .argument { 27 | color: #DCDCDC; 28 | } 29 | 30 | pre .number, 31 | pre .date { 32 | color: #8CD0D3; 33 | } 34 | 35 | pre .dos .envvar, 36 | pre .dos .stream, 37 | pre .variable, 38 | pre .apache .sqbracket { 39 | color: #EFDCBC; 40 | } 41 | 42 | pre .dos .flow, 43 | pre .diff .change, 44 | pre .python .exception, 45 | pre .python .built_in, 46 | pre .literal, 47 | pre .tex .special { 48 | color: #EFEFAF; 49 | } 50 | 51 | pre .diff .chunk, 52 | pre .ruby .subst { 53 | color: #8F8F8F; 54 | } 55 | 56 | pre .dos .keyword, 57 | pre .python .decorator, 58 | pre .class .title, 59 | pre .haskell .label, 60 | pre .function .title, 61 | pre .ini .title, 62 | pre .diff .header, 63 | pre .ruby .class .parent, 64 | pre .apache .tag, 65 | pre .nginx .built_in, 66 | pre .tex .command, 67 | pre .input_number { 68 | color: #efef8f; 69 | } 70 | 71 | pre .dos .winutils, 72 | pre .ruby .symbol, 73 | pre .ruby .symbol .string, 74 | pre .ruby .symbol .keyword, 75 | pre .ruby .symbol .keymethods, 76 | pre .ruby .string, 77 | pre .ruby .instancevar { 78 | color: #DCA3A3; 79 | } 80 | 81 | pre .diff .deletion, 82 | pre .string, 83 | pre .tag .value, 84 | pre .preprocessor, 85 | pre .built_in, 86 | pre .sql .aggregate, 87 | pre .javadoc, 88 | pre .smalltalk .class, 89 | pre .smalltalk .localvars, 90 | pre .smalltalk .array, 91 | pre .css .rules .value, 92 | pre .attr_selector, 93 | pre .pseudo, 94 | pre .apache .cbracket, 95 | pre .tex .formula { 96 | color: #CC9393; 97 | } 98 | 99 | pre .shebang, 100 | pre .diff .addition, 101 | pre .comment, 102 | pre .java .annotation, 103 | pre .template_comment, 104 | pre .pi, 105 | pre .doctype { 106 | color: #7F9F7F; 107 | } 108 | 109 | pre .xml .css, 110 | pre .xml .javascript, 111 | pre .xml .vbscript, 112 | pre .tex .formula { 113 | opacity: 0.5; 114 | } 115 | 116 | -------------------------------------------------------------------------------- /presentacion_curso_2014/reveal.js/js/head.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | Head JS The only script in your 3 | Copyright Tero Piirainen (tipiirai) 4 | License MIT / http://bit.ly/mit-license 5 | Version 0.96 6 | 7 | http://headjs.com 8 | */(function(a){function z(){d||(d=!0,s(e,function(a){p(a)}))}function y(c,d){var e=a.createElement("script");e.type="text/"+(c.type||"javascript"),e.src=c.src||c,e.async=!1,e.onreadystatechange=e.onload=function(){var a=e.readyState;!d.done&&(!a||/loaded|complete/.test(a))&&(d.done=!0,d())},(a.body||b).appendChild(e)}function x(a,b){if(a.state==o)return b&&b();if(a.state==n)return k.ready(a.name,b);if(a.state==m)return a.onpreload.push(function(){x(a,b)});a.state=n,y(a.url,function(){a.state=o,b&&b(),s(g[a.name],function(a){p(a)}),u()&&d&&s(g.ALL,function(a){p(a)})})}function w(a,b){a.state===undefined&&(a.state=m,a.onpreload=[],y({src:a.url,type:"cache"},function(){v(a)}))}function v(a){a.state=l,s(a.onpreload,function(a){a.call()})}function u(a){a=a||h;var b;for(var c in a){if(a.hasOwnProperty(c)&&a[c].state!=o)return!1;b=!0}return b}function t(a){return Object.prototype.toString.call(a)=="[object Function]"}function s(a,b){if(!!a){typeof a=="object"&&(a=[].slice.call(a));for(var c=0;c 3 | Copyright Tero Piirainen (tipiirai) 4 | License MIT / http://bit.ly/mit-license 5 | Version 0.96 6 | 7 | http://headjs.com 8 | */(function(a){function z(){d||(d=!0,s(e,function(a){p(a)}))}function y(c,d){var e=a.createElement("script");e.type="text/"+(c.type||"javascript"),e.src=c.src||c,e.async=!1,e.onreadystatechange=e.onload=function(){var a=e.readyState;!d.done&&(!a||/loaded|complete/.test(a))&&(d.done=!0,d())},(a.body||b).appendChild(e)}function x(a,b){if(a.state==o)return b&&b();if(a.state==n)return k.ready(a.name,b);if(a.state==m)return a.onpreload.push(function(){x(a,b)});a.state=n,y(a.url,function(){a.state=o,b&&b(),s(g[a.name],function(a){p(a)}),u()&&d&&s(g.ALL,function(a){p(a)})})}function w(a,b){a.state===undefined&&(a.state=m,a.onpreload=[],y({src:a.url,type:"cache"},function(){v(a)}))}function v(a){a.state=l,s(a.onpreload,function(a){a.call()})}function u(a){a=a||h;var b;for(var c in a){if(a.hasOwnProperty(c)&&a[c].state!=o)return!1;b=!0}return b}function t(a){return Object.prototype.toString.call(a)=="[object Function]"}function s(a,b){if(!!a){typeof a=="object"&&(a=[].slice.call(a));for(var c=0;c 65 | print "Event handler `OnClick' not implemented!" 66 | nclaves_1 = int(self.nclaves.GetValue()) 67 | lclave_1 = int(self.lclave.GetValue()) 68 | valores = u"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@#%&" 69 | p = u"" 70 | 71 | f = open('Claves_generadas','a') 72 | for x in range(nclaves_1): 73 | 74 | data = p.join([choice(valores) for i in range(lclave_1)]) 75 | data 76 | 77 | print (data+u"\n") 78 | 79 | 80 | f.write(data+"\n") 81 | f.close() 82 | m = "Listo" 83 | print (m) 84 | self.salida.SetValue(m) 85 | 86 | #event.Skip() 87 | 88 | # end of class MyFrame 89 | if __name__ == "__main__": 90 | app = wx.PySimpleApp(0) 91 | wx.InitAllImageHandlers() 92 | frame_1 = MyFrame(None, -1, "") 93 | app.SetTopWindow(frame_1) 94 | frame_1.Show() 95 | app.MainLoop() 96 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | 46 | [Dd]ebug/ 47 | [Rr]elease/ 48 | x64/ 49 | build/ 50 | [Bb]in/ 51 | [Oo]bj/ 52 | 53 | # MSTest test Results 54 | [Tt]est[Rr]esult*/ 55 | [Bb]uild[Ll]og.* 56 | 57 | *_i.c 58 | *_p.c 59 | *.ilk 60 | *.meta 61 | *.obj 62 | *.pch 63 | *.pdb 64 | *.pgc 65 | *.pgd 66 | *.rsp 67 | *.sbr 68 | *.tlb 69 | *.tli 70 | *.tlh 71 | *.tmp 72 | *.tmp_proj 73 | *.log 74 | *.vspscc 75 | *.vssscc 76 | .builds 77 | *.pidb 78 | *.log 79 | *.scc 80 | 81 | # Visual C++ cache files 82 | ipch/ 83 | *.aps 84 | *.ncb 85 | *.opensdf 86 | *.sdf 87 | *.cachefile 88 | 89 | # Visual Studio profiler 90 | *.psess 91 | *.vsp 92 | *.vspx 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | 101 | # TeamCity is a build add-in 102 | _TeamCity* 103 | 104 | # DotCover is a Code Coverage Tool 105 | *.dotCover 106 | 107 | # NCrunch 108 | *.ncrunch* 109 | .*crunch*.local.xml 110 | 111 | # Installshield output folder 112 | [Ee]xpress/ 113 | 114 | # DocProject is a documentation generator add-in 115 | DocProject/buildhelp/ 116 | DocProject/Help/*.HxT 117 | DocProject/Help/*.HxC 118 | DocProject/Help/*.hhc 119 | DocProject/Help/*.hhk 120 | DocProject/Help/*.hhp 121 | DocProject/Help/Html2 122 | DocProject/Help/html 123 | 124 | # Click-Once directory 125 | publish/ 126 | 127 | # Publish Web Output 128 | *.Publish.xml 129 | *.pubxml 130 | 131 | # NuGet Packages Directory 132 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 133 | #packages/ 134 | 135 | # Windows Azure Build Output 136 | csx 137 | *.build.csdef 138 | 139 | # Windows Store app package directory 140 | AppPackages/ 141 | 142 | # Others 143 | sql/ 144 | *.Cache 145 | ClientBin/ 146 | [Ss]tyle[Cc]op.* 147 | ~$* 148 | *~ 149 | *.dbmdl 150 | *.[Pp]ublish.xml 151 | *.pfx 152 | *.publishsettings 153 | 154 | # RIA/Silverlight projects 155 | Generated_Code/ 156 | 157 | # Backup & report files from converting an old project file to a newer 158 | # Visual Studio version. Backup files are not needed, because we have git ;-) 159 | _UpgradeReport_Files/ 160 | Backup*/ 161 | UpgradeLog*.XML 162 | UpgradeLog*.htm 163 | 164 | # SQL Server files 165 | App_Data/*.mdf 166 | App_Data/*.ldf 167 | 168 | ############# 169 | ## Windows detritus 170 | ############# 171 | 172 | # Windows image file caches 173 | Thumbs.db 174 | ehthumbs.db 175 | 176 | # Folder config file 177 | Desktop.ini 178 | 179 | # Recycle Bin used on file shares 180 | $RECYCLE.BIN/ 181 | 182 | # Mac crap 183 | .DS_Store 184 | 185 | 186 | ############# 187 | ## Python 188 | ############# 189 | 190 | *.py[co] 191 | 192 | # Packages 193 | *.egg 194 | *.egg-info 195 | dist/ 196 | build/ 197 | eggs/ 198 | parts/ 199 | var/ 200 | sdist/ 201 | develop-eggs/ 202 | .installed.cfg 203 | 204 | # Installer logs 205 | pip-log.txt 206 | 207 | # Unit test / coverage reports 208 | .coverage 209 | .tox 210 | 211 | #Translations 212 | *.mo 213 | 214 | #Mr Developer 215 | .mr.developer.cfg 216 | -------------------------------------------------------------------------------- /presentacion_curso_2014/styles/numericalmoocstyle.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 126 | 142 | -------------------------------------------------------------------------------- /presentacion_curso_2014/reveal.js/css/theme/simple.css: -------------------------------------------------------------------------------- 1 | @import url(https://fonts.googleapis.com/css?family=News+Cycle:400,700); 2 | @import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); 3 | /** 4 | * A simple theme for reveal.js presentations, similar 5 | * to the default theme. The accent color is darkblue. 6 | * 7 | * This theme is Copyright (C) 2012 Owen Versteeg, https://github.com/StereotypicalApps. It is MIT licensed. 8 | * reveal.js is Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se 9 | */ 10 | /********************************************* 11 | * GLOBAL STYLES 12 | *********************************************/ 13 | body { 14 | background: white; 15 | background-color: white; } 16 | 17 | .reveal { 18 | font-family: "Lato", sans-serif; 19 | font-size: 36px; 20 | font-weight: 200; 21 | letter-spacing: -0.02em; 22 | color: black; } 23 | 24 | ::selection { 25 | color: white; 26 | background: rgba(0, 0, 0, 0.99); 27 | text-shadow: none; } 28 | 29 | /********************************************* 30 | * HEADERS 31 | *********************************************/ 32 | .reveal h1, 33 | .reveal h2, 34 | .reveal h3, 35 | .reveal h4, 36 | .reveal h5, 37 | .reveal h6 { 38 | margin: 0 0 20px 0; 39 | color: black; 40 | font-family: "News Cycle", Impact, sans-serif; 41 | line-height: 0.9em; 42 | letter-spacing: 0.02em; 43 | text-transform: none; 44 | text-shadow: none; } 45 | 46 | .reveal h1 { 47 | text-shadow: 0px 0px 6px rgba(0, 0, 0, 0.2); } 48 | 49 | /********************************************* 50 | * LINKS 51 | *********************************************/ 52 | .reveal a:not(.image) { 53 | color: darkblue; 54 | text-decoration: none; 55 | -webkit-transition: color .15s ease; 56 | -moz-transition: color .15s ease; 57 | -ms-transition: color .15s ease; 58 | -o-transition: color .15s ease; 59 | transition: color .15s ease; } 60 | 61 | .reveal a:not(.image):hover { 62 | color: #0000f1; 63 | text-shadow: none; 64 | border: none; } 65 | 66 | .reveal .roll span:after { 67 | color: #fff; 68 | background: #00003f; } 69 | 70 | /********************************************* 71 | * IMAGES 72 | *********************************************/ 73 | .reveal section img { 74 | margin: 15px 0px; 75 | background: rgba(255, 255, 255, 0.12); 76 | border: 4px solid black; 77 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); 78 | -webkit-transition: all .2s linear; 79 | -moz-transition: all .2s linear; 80 | -ms-transition: all .2s linear; 81 | -o-transition: all .2s linear; 82 | transition: all .2s linear; } 83 | 84 | .reveal a:hover img { 85 | background: rgba(255, 255, 255, 0.2); 86 | border-color: darkblue; 87 | box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); } 88 | 89 | /********************************************* 90 | * NAVIGATION CONTROLS 91 | *********************************************/ 92 | .reveal .controls div.navigate-left, 93 | .reveal .controls div.navigate-left.enabled { 94 | border-right-color: darkblue; } 95 | 96 | .reveal .controls div.navigate-right, 97 | .reveal .controls div.navigate-right.enabled { 98 | border-left-color: darkblue; } 99 | 100 | .reveal .controls div.navigate-up, 101 | .reveal .controls div.navigate-up.enabled { 102 | border-bottom-color: darkblue; } 103 | 104 | .reveal .controls div.navigate-down, 105 | .reveal .controls div.navigate-down.enabled { 106 | border-top-color: darkblue; } 107 | 108 | .reveal .controls div.navigate-left.enabled:hover { 109 | border-right-color: #0000f1; } 110 | 111 | .reveal .controls div.navigate-right.enabled:hover { 112 | border-left-color: #0000f1; } 113 | 114 | .reveal .controls div.navigate-up.enabled:hover { 115 | border-bottom-color: #0000f1; } 116 | 117 | .reveal .controls div.navigate-down.enabled:hover { 118 | border-top-color: #0000f1; } 119 | 120 | /********************************************* 121 | * PROGRESS BAR 122 | *********************************************/ 123 | .reveal .progress { 124 | background: rgba(0, 0, 0, 0.2); } 125 | 126 | .reveal .progress span { 127 | background: darkblue; 128 | -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); 129 | -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); 130 | -ms-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); 131 | -o-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); 132 | transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } 133 | -------------------------------------------------------------------------------- /14.10.2014-Charla_1/WxPython/Ejemplos/Flujo calor/Cuadro.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # generated by wxGlade 0.6.4 on Wed Apr 9 13:14:56 2014 3 | 4 | import wx 5 | from numpy import * 6 | import matplotlib.pyplot as plt 7 | from matplotlib.figure import Figure 8 | from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas 9 | from mpl_toolkits.mplot3d import Axes3D 10 | # begin wxGlade: dependencies 11 | from MplPanel_1 import MplPanel_1 12 | from MplPanel_2 import MplPanel_2 13 | # end wxGlade 14 | 15 | # begin wxGlade: extracode 16 | 17 | # end wxGlade 18 | 19 | 20 | class Cuadro(wx.Frame): 21 | def __init__(self, *args, **kwds): 22 | # begin wxGlade: Cuadro.__init__ 23 | kwds["style"] = wx.DEFAULT_FRAME_STYLE 24 | wx.Frame.__init__(self, *args, **kwds) 25 | self.label_1 = wx.StaticText(self, -1, "Mach mínimo", style=wx.ALIGN_CENTRE) 26 | self.Mmin = wx.TextCtrl(self, -1, "0") 27 | self.label_4 = wx.StaticText(self, -1, "Altitud mínima [m]", style=wx.ALIGN_CENTRE) 28 | self.hmin = wx.TextCtrl(self, -1, "0") 29 | self.label_7 = wx.StaticText(self, -1, "Diámetro del borde de ataque [m]", style=wx.ALIGN_CENTRE) 30 | self.diam = wx.TextCtrl(self, -1, "0.2") 31 | self.label_2 = wx.StaticText(self, -1, "Mach máximo", style=wx.ALIGN_CENTRE) 32 | self.Mmax = wx.TextCtrl(self, -1, "1") 33 | self.label_5 = wx.StaticText(self, -1, "Altitud máxima [m]", style=wx.ALIGN_CENTRE) 34 | self.hmax = wx.TextCtrl(self, -1, "11000") 35 | self.label_8 = wx.StaticText(self, -1, "Altura mojada del perfil [m]", style=wx.ALIGN_CENTRE) 36 | self.altcorte = wx.TextCtrl(self, -1, "0.3") 37 | self.label_3 = wx.StaticText(self, -1, "Número de puntos mallado", style=wx.ALIGN_CENTRE) 38 | self.mallado = wx.TextCtrl(self, -1, "120") 39 | self.label_6 = wx.StaticText(self, -1, "Longitud mojada del perfil [m]", style=wx.ALIGN_CENTRE) 40 | self.longi = wx.TextCtrl(self, -1, "1.02") 41 | self.label_9 = wx.StaticText(self, -1, "Semienvergadura [m]", style=wx.ALIGN_CENTRE) 42 | self.semienvergadura = wx.TextCtrl(self, -1, "6") 43 | self.boton = wx.Button(self, -1, "Calcular") 44 | self.curvas = MplPanel_1(self, -1) 45 | self.superficie = MplPanel_2(self, -1) 46 | 47 | self.__set_properties() 48 | self.__do_layout() 49 | 50 | self.Bind(wx.EVT_BUTTON, self.onclick, self.boton) 51 | # end wxGlade 52 | 53 | def __set_properties(self): 54 | # begin wxGlade: Cuadro.__set_properties 55 | self.SetTitle("Cálculo de la convección exterior y flujo de calor por agua impactada") 56 | # end wxGlade 57 | 58 | def __do_layout(self): 59 | # begin wxGlade: Cuadro.__do_layout 60 | sizer_1 = wx.BoxSizer(wx.VERTICAL) 61 | sizer_2 = wx.BoxSizer(wx.HORIZONTAL) 62 | grid_sizer_1 = wx.GridSizer(4, 6, 2, 2) 63 | grid_sizer_1.Add(self.label_1, 0, 0, 0) 64 | grid_sizer_1.Add(self.Mmin, 0, 0, 0) 65 | grid_sizer_1.Add(self.label_4, 0, 0, 0) 66 | grid_sizer_1.Add(self.hmin, 0, 0, 0) 67 | grid_sizer_1.Add(self.label_7, 0, 0, 0) 68 | grid_sizer_1.Add(self.diam, 0, 0, 0) 69 | grid_sizer_1.Add(self.label_2, 0, 0, 0) 70 | grid_sizer_1.Add(self.Mmax, 0, 0, 0) 71 | grid_sizer_1.Add(self.label_5, 0, 0, 0) 72 | grid_sizer_1.Add(self.hmax, 0, 0, 0) 73 | grid_sizer_1.Add(self.label_8, 0, 0, 0) 74 | grid_sizer_1.Add(self.altcorte, 0, 0, 0) 75 | grid_sizer_1.Add(self.label_3, 0, 0, 0) 76 | grid_sizer_1.Add(self.mallado, 0, 0, 0) 77 | grid_sizer_1.Add(self.label_6, 0, 0, 0) 78 | grid_sizer_1.Add(self.longi, 0, 0, 0) 79 | grid_sizer_1.Add(self.label_9, 0, 0, 0) 80 | grid_sizer_1.Add(self.semienvergadura, 0, 0, 0) 81 | grid_sizer_1.Add((10, 10), 0, 0, 0) 82 | grid_sizer_1.Add((10, 10), 0, 0, 0) 83 | grid_sizer_1.Add((10, 10), 0, 0, 0) 84 | grid_sizer_1.Add((10, 10), 0, 0, 0) 85 | grid_sizer_1.Add((10, 10), 0, 0, 0) 86 | grid_sizer_1.Add(self.boton, 0, 0, 0) 87 | sizer_1.Add(grid_sizer_1, 0, wx.EXPAND, 0) 88 | sizer_2.Add(self.curvas, 1, wx.EXPAND, 0) 89 | sizer_2.Add(self.superficie, 1, wx.EXPAND, 0) 90 | sizer_1.Add(sizer_2, 1, wx.EXPAND, 0) 91 | self.SetSizer(sizer_1) 92 | sizer_1.Fit(self) 93 | self.Layout() 94 | # end wxGlade 95 | 96 | def onclick(self, event): # wxGlade: Cuadro. 97 | 98 | Cp = 1004 99 | hfg = 2300 100 | Ka = 0.02 101 | Kal = 209 102 | R = 287 103 | g = 9.81 104 | 105 | 106 | ########Lectura de datos#################################### 107 | try: 108 | D = float(self.diam.GetValue()) 109 | Sw = float(self.longi.GetValue()) 110 | mallado = float(self.mallado.GetValue()) 111 | H = float(self.altcorte.GetValue()) 112 | 113 | L = float(self.semienvergadura.GetValue()) 114 | 115 | Mmin = float(self.Mmin.GetValue()) 116 | Mmax = float(self.Mmax.GetValue()) 117 | 118 | hmin = float(self.hmin.GetValue()) 119 | hmax = float(self.hmax.GetValue()) 120 | except: 121 | dlg = wx.MessageDialog(self, "Introduce valores numéricos", 122 | "Error", wx.OK) 123 | dlg.ShowModal() 124 | dlg.Destroy() 125 | return 126 | ############################################################# 127 | 128 | m = linspace(Mmin,Mmax,mallado) 129 | h = linspace(hmin,hmax,mallado) 130 | 131 | 132 | mm,hh = meshgrid(m,h) 133 | 134 | 135 | Tinf = 288.15-0.0065*hh 136 | 137 | a = (1.4*R*Tinf)**0.5 138 | 139 | mu = 0.00001827*((291.15+120)/(Tinf+120))*(Tinf/291.15)**1.5 140 | 141 | Pr = mu*Cp/Ka 142 | 143 | rPr = Pr**0.5 144 | 145 | Ttinf = Tinf*(1+0.2*rPr*mm**2) 146 | 147 | ro = 1.225*((288.15-0.0065*hh)/288.15)**((g/(R*0.0065))-1) 148 | 149 | he = Ka*0.57*Pr**(0.4)*((4*ro*mm*(1.4*R*Tinf)**0.5)/(mu*D))**0.5 150 | 151 | LWC = 1.478750e-8*hh**2-2.418000e-04*hh+1.082250 #Revisar!!!!! 152 | 153 | Uinf = mm*a 154 | 155 | mw = LWC/1000*H*L*Uinf 156 | 157 | qw = mw/(L*Sw)*hfg 158 | 159 | #PAnel izquierdo 160 | self.curvas.figure.clf() 161 | self.curvas.axes = self.curvas.figure.add_subplot(111) 162 | cs = self.curvas.axes.contour(m, h, he, 25) 163 | self.curvas.axes.clabel(cs, inline=1, fontside=10) 164 | self.curvas.axes.set_title('Conveccion exterior') 165 | self.curvas.canvas.draw() 166 | 167 | 168 | #Panel derecho 169 | self.superficie.figure.clf() 170 | ax = self.superficie.figure.add_subplot(111, projection='3d') 171 | ax.plot_surface(mm, hh,qw , rstride=8, cstride=8, alpha=0.3) 172 | cset = ax.contourf(mm, hh, qw, 20, zdir='z', offset=0) 173 | cset = ax.contourf(mm, hh, qw, 20, zdir='x', offset=0) 174 | cset = ax.contourf(mm, hh, qw, 20, zdir='y', offset=0,) 175 | 176 | 177 | ax.set_xlabel('Mach') 178 | ax.set_xlim(0, 1) 179 | ax.set_ylabel('Altitud') 180 | ax.set_ylim(0, 11000) 181 | ax.set_zlabel('Calor agua') 182 | ax.set_zlim(0, 250) 183 | 184 | ax.set_title('Flujo calor agua ingerida') 185 | 186 | self.superficie.canvas.draw() 187 | 188 | event.Skip() 189 | 190 | # end of class Cuadro 191 | -------------------------------------------------------------------------------- /14.10.2014-Charla_1/Animaciones_matplotlib-Albertp_Lorenzo.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "name": "", 4 | "signature": "sha256:95558caab2b820765fb3097143fc37147e2dfc018cb9fc71c3b49dd62e0aea64" 5 | }, 6 | "nbformat": 3, 7 | "nbformat_minor": 0, 8 | "worksheets": [ 9 | { 10 | "cells": [ 11 | { 12 | "cell_type": "heading", 13 | "level": 1, 14 | "metadata": {}, 15 | "source": [ 16 | "*** Animaciones ***" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": {}, 22 | "source": [ 23 | "El presente documento se distribuye bajo una licencia [Licencia Creative Commons Atribuci\u00f3n 4.0 Internacional](http://creativecommons.org/licenses/by/4.0/deed.es). Realizado el 10/04/2014 por Alberto Lorenzo ([@newlawrence](https://twitter.com/newlawrence))\n", 24 | "\n", 25 | "Para este *notebook* se ha utilizado:\n", 26 | "\n", 27 | "> ***Python 3.3.5***\n", 28 | "\n", 29 | "> ***numpy 1.8.0***\n", 30 | "\n", 31 | "> ***matplotlib 1.3.1***\n", 32 | "\n", 33 | "> ***IPython 2.0.0***" 34 | ] 35 | }, 36 | { 37 | "cell_type": "heading", 38 | "level": 3, 39 | "metadata": {}, 40 | "source": [ 41 | "Introducci\u00f3n" 42 | ] 43 | }, 44 | { 45 | "cell_type": "markdown", 46 | "metadata": {}, 47 | "source": [ 48 | "De todas las variables matem\u00e1ticas de nuestras f\u00f3rmulas, probablemente ninguna tenga un significado m\u00e1s especial que la temporal. El tiempo, dif\u00edcil de definir, solemos describirlo como aquello que, simplemente, pasa. Tener una ecuaci\u00f3n matem\u00e1tica que describa la evoluci\u00f3n de un sistema o magnitud est\u00e1 bien... pero no hay nada como ver dicha evoluci\u00f3n en tiempo real.\n", 49 | "\n", 50 | "Las animaciones son un poderoso reclamo visual para nuestros trabajos y veremos que *Matplotlib* nos permitir\u00e1 realizarlas de una manera r\u00e1pida y sencilla" 51 | ] 52 | }, 53 | { 54 | "cell_type": "heading", 55 | "level": 4, 56 | "metadata": {}, 57 | "source": [ 58 | "\u00bfQu\u00e9 necesitamos?" 59 | ] 60 | }, 61 | { 62 | "cell_type": "markdown", 63 | "metadata": {}, 64 | "source": [ 65 | "Como m\u00ednimo, s\u00f3lo una funci\u00f3n de *Matplotlib*, bueno... dos; una de *Matplotlib* y otra que definiremos nosotros. Pero vayamos paso por paso.\n", 66 | "\n", 67 | "El m\u00f3dulo *animation* de *Matplotlib* provee herramientas tremendamente potentes y flexibles, pero nosotros s\u00f3lo haremos uso de la funci\u00f3n **FuncAnimation**, que es la de m\u00e1s alto nivel (y por lo tanto m\u00e1s sencilla) y que nos servir\u00e1 para crear casi cualquier cosa que seamos capaces de imaginar.\n", 68 | "\n", 69 | "Comencemos importando todo lo necesario:" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "collapsed": false, 75 | "input": [ 76 | "# Primero las bibliotecas b\u00e1sicas de toda la vida\n", 77 | "import numpy as np\n", 78 | "import matplotlib.pyplot as plt\n", 79 | "\n", 80 | "import matplotlib as mpl # Para importar las clases colormap y colorbar\n", 81 | "\n", 82 | "# Unas cuantas figuritas vistosas\n", 83 | "from matplotlib.patches import Circle\n", 84 | "from matplotlib.patches import FancyArrowPatch\n", 85 | "\n", 86 | "# Nuestra funci\u00f3n principal\n", 87 | "from matplotlib.animation import FuncAnimation" 88 | ], 89 | "language": "python", 90 | "metadata": {}, 91 | "outputs": [], 92 | "prompt_number": 1 93 | }, 94 | { 95 | "cell_type": "heading", 96 | "level": 4, 97 | "metadata": {}, 98 | "source": [ 99 | "FuncAnimation" 100 | ] 101 | }, 102 | { 103 | "cell_type": "markdown", 104 | "metadata": {}, 105 | "source": [ 106 | "La funci\u00f3n **FuncAnimation** devuelve un objeto que contiene toda la informaci\u00f3n necesaria para representar en pantalla y guardar nuestra animaci\u00f3n en un fichero. La especificaci\u00f3n completa puede consultarse en su docstring:" 107 | ] 108 | }, 109 | { 110 | "cell_type": "code", 111 | "collapsed": false, 112 | "input": [ 113 | "FuncAnimation?" 114 | ], 115 | "language": "python", 116 | "metadata": {}, 117 | "outputs": [], 118 | "prompt_number": 2 119 | }, 120 | { 121 | "cell_type": "markdown", 122 | "metadata": {}, 123 | "source": [ 124 | "De todas formas, vamos a comentar un poquillo los argumentos con los que se la suele llamar normalmente:\n", 125 | "\n", 126 | "Los dos primeros argumentos son obligatorios, el primero es la figura de *Matplotlib* que queremos animar y el segundo es una funci\u00f3n definida por nosotros de la que ahora hablaremos.\n", 127 | "\n", 128 | "Los par\u00e1metros adicionales a especificar son:\n", 129 | "\n", 130 | "> **frames**: es el n\u00famero de cuadros que va a contener nuestra animaci\u00f3n (el n\u00famero de capturas vamos, a los aficionados a los videojuegos no les pillar\u00e1 de nuevas).\n", 131 | "\n", 132 | "> **interval**: el espacio de tiempo en milisegundos entre capturas.\n", 133 | "\n", 134 | "> **blit**: es una bandera que si se establece en verdadero refresca s\u00f3lo los objetos especificados en vez de repintar la figura entera. Aunque a veces es un poco rollo, es conveniente que hagamos nuestras animaciones con el blitting activado para reducir la carga del ordenador y que estas puedan verse m\u00e1s flu\u00eddas.\n", 135 | "\n", 136 | "> **repeat**: es una bandera que por defecto est\u00e1 activada con el valor True. Hace que nuestra animaci\u00f3n se repita en bucle ignorando el valor dado a frames (s\u00f3lo afecta a la animaci\u00f3n desde nuestro c\u00f3digo Python, si la exportamos a un archivo de v\u00eddeo se mantienen los frames especificados).\n", 137 | "\n", 138 | "Ejemplo de llamada:\n", 139 | "\n", 140 | "***mi_objeto_animacion*** = **FuncAnimation**(*nuestra_figura*, *nuestra_funcion_animadora*, *frames=1000*, *interval=50*, *blit=True*, *repeat=False*)" 141 | ] 142 | }, 143 | { 144 | "cell_type": "markdown", 145 | "metadata": {}, 146 | "source": [ 147 | "La funci\u00f3n que hemos de definir nosotros ha de aceptar un \u00fanico argumento, que FuncAnimation se encargar\u00e1 de ir rellenando con una sucesi\u00f3n de n\u00fameros enteros en cada llamada ***(0, 0, 1, 2, 3, 4...)*** (el motivo del doble 0, es que **FuncAnimation** necesita de un cuadro inicial, pero ya hablaremos de ello cuando lo necesitemos). Por otro lad, **si hemos activado el *blitting*, nuestra funci\u00f3n deber\u00e1 retornar una lista con todos los objetos que han de ser actualizados**.\n", 148 | "\n", 149 | "La interfaz de nuestra funci\u00f3n ser\u00e1 pues:\n", 150 | "***def*** **mi_funcion_animadora**(*contador*):\n", 151 | "\n", 152 | "Para entender c\u00f3mo se ha de definir esta funci\u00f3n, procedamos con algunos ejemplos." 153 | ] 154 | }, 155 | { 156 | "cell_type": "heading", 157 | "level": 2, 158 | "metadata": {}, 159 | "source": [ 160 | "Ejemplos" 161 | ] 162 | }, 163 | { 164 | "cell_type": "heading", 165 | "level": 3, 166 | "metadata": {}, 167 | "source": [ 168 | "Animando sin(x)" 169 | ] 170 | }, 171 | { 172 | "cell_type": "markdown", 173 | "metadata": {}, 174 | "source": [ 175 | "Este ejemplo es muy sencillo. El procedimiento es id\u00e9ntico al que seguimos para pintar la gr\u00e1fica de la funci\u00f3n seno, s\u00f3lo que esta vez pintaremos una l\u00ednea vac\u00eda (que luego iremos rellenando con valores a medida que los calculamos).\n", 176 | "\n", 177 | "Un momento... \u00bfl\u00edneas vac\u00edas? Pues s\u00ed, haciendo: **plot**([], []). La funci\u00f3n **plot** de *Matplotlib* devuelve una lista con las referencias a todas las l\u00edneas que pinta, as\u00ed que podemos guardar dichas referencias en variables para luego poder actualizar las l\u00edneas a medida que vayamos calculando datos.\n", 178 | "\n", 179 | "Hay que recordar que **plot** devuelve una lista, as\u00ed que como s\u00f3lo vamos a pintar una l\u00ednea queremos la referencia del primer elemento cosa que podemos hacer de dos maneras (\u00a1ojo, que es muy f\u00e1cil olvidarse de esto!):\n", 180 | "\n", 181 | "> linea, = **plot**(...)\n", 182 | "\n", 183 | "> linea = **plot**(...)[0]" 184 | ] 185 | }, 186 | { 187 | "cell_type": "code", 188 | "collapsed": false, 189 | "input": [ 190 | "# Creamos nuestro gr\u00e1fico y unos ejes\n", 191 | "fig, ax = plt.subplots()\n", 192 | "ax.set_xlim(0, 360)\n", 193 | "ax.set_ylim(-2., 2.)\n", 194 | "\n", 195 | "# Creamos una l\u00ednea vac\u00eda y guardamos la referencia\n", 196 | "line, = ax.plot([], [], linewidth=2)\n", 197 | "\n", 198 | "\n", 199 | "def animate(ii):\n", 200 | " ''' Esta es la funci\u00f3n principal.\n", 201 | " \n", 202 | " Vamos a aprovechar que FuncAnimation asigna n\u00fameros enteros a ii, y vamos a dibujar la gr\u00e1fica utilizando\n", 203 | " grados cent\u00edgrados en lugar de radianes. Utilizando el operador resto (%), nos aseguramos que ii vaya tomando\n", 204 | " valores de 0 a 359 de forma c\u00edclica, haciendo que nuestra animaci\u00f3n se repita de forma peri\u00f3dica\n", 205 | " '''\n", 206 | " \n", 207 | " # Creamos los vectores con los valores de x e y\n", 208 | " # Como puede verse, en cada llamada se va incrementando el n\u00famero de puntos haciendo avanzar la gr\u00e1fica \"hacia adelante\"\n", 209 | " x = np.linspace(0, ii % 360, 100)\n", 210 | " y = np.sin(np.deg2rad(x))\n", 211 | " \n", 212 | " line.set_data(x, y) # Esta es la forma en que actualizamos la informaci\u00f3n de las l\u00edneas\n", 213 | "\n", 214 | " # No olvidemos retornar nuestro objeto linea o de lo contrario \u00a1\u00a1no se actualizar\u00e1!!\n", 215 | " return line,\n", 216 | "\n", 217 | "# Llamamos a FuncAnimation\n", 218 | "animation = FuncAnimation(fig, animate, frames=3600, interval=50, blit=True)\n", 219 | "\n", 220 | "plt.show() # Para que matplotlib genere el gr\u00e1fico mostrando la animaci\u00f3n\n" 221 | ], 222 | "language": "python", 223 | "metadata": {}, 224 | "outputs": [], 225 | "prompt_number": 3 226 | }, 227 | { 228 | "cell_type": "heading", 229 | "level": 3, 230 | "metadata": {}, 231 | "source": [ 232 | "Onda senoidal" 233 | ] 234 | }, 235 | { 236 | "cell_type": "markdown", 237 | "metadata": {}, 238 | "source": [ 239 | "Vamos a dar una vuelta de tuerca. De matem\u00e1ticas sabemos que sumando una constante a la variable independiente de una funci\u00f3n de una \u00fanica variable desplazamos esta hacia la izquierda. Podemos usar este efecto para trasladar nuestra funci\u00f3n seno en cada cuadro y simular una onda viajera.\n", 240 | "\n", 241 | "En este caso, nos vamos a encontrar con una dificultad. Como vimos, **FuncAnimation** va a dar los valores ***(0, 0, 1, 2, 3...)*** a la variable que utilice como contador. Ese primer ***0***, lo utilizar\u00e1 para calcular el primer cuadro a partir de cu\u00e1l ir\u00e1 repintando. Eso significa que aunque veamos un seno desplaz\u00e1ndose en nuestra animaci\u00f3n, de fondo quedar\u00e1 la funci\u00f3n original est\u00e1tica estropeando el resultado.\n", 242 | "\n", 243 | "Como lo que normalmente querremos es que el cuadro sobre el cu\u00e1l se va a dibujar est\u00e9 en blanco, podemos definir una funci\u00f3n inicial que **FuncAnimation** se encargar\u00e1 de utilizar. Utilizaremos esta funci\u00f3n inicial para establecer los elementos a redibujar al valor que queramos, en lugar del calculado por **FuncAnimation** con el valor *contador=0*." 244 | ] 245 | }, 246 | { 247 | "cell_type": "code", 248 | "collapsed": false, 249 | "input": [ 250 | "# Creamos nuestro gr\u00e1fico y unos ejes\n", 251 | "fig, ax = plt.subplots()\n", 252 | "ax.set_xlim(-180, 180)\n", 253 | "ax.set_ylim(-2., 2.)\n", 254 | "\n", 255 | "# Creamos una l\u00ednea vac\u00eda y guardamos la referencia\n", 256 | "line, = ax.plot([], [], linewidth=2)\n", 257 | "\n", 258 | "\n", 259 | "def init():\n", 260 | " ''' Esta funci\u00f3n se llamar\u00e1 para pintar el primer cuadro.\n", 261 | " '''\n", 262 | " \n", 263 | " # Como queremos que el primer cuadro est\u00e9 vac\u00edo\n", 264 | " line.set_data([], [])\n", 265 | " \n", 266 | " # Que no se nosolvide retornar los elementos que han de actualizarse\n", 267 | " return line,\n", 268 | "\n", 269 | "\n", 270 | "def animate(ii):\n", 271 | " ''' Ahora lo que hacemos es crear un vector x de una amplitud mayor a nuestro dominio, que en cada paso iremos\n", 272 | " incrementando hasta alcanzar 360\u00ba.\n", 273 | " '''\n", 274 | " \n", 275 | " # Creamos los vectores con los valores de x y de y\n", 276 | " x = np.linspace(-540, 180, 100)\n", 277 | " delta_x = (x.max() - x.min()) / x.size\n", 278 | " y = np.sin(np.deg2rad(x + (ii * delta_x) % 360))\n", 279 | " \n", 280 | " # Actualizamos la informaci\u00f3n de nuestro objeto l\u00ednea\n", 281 | " line.set_data(x, y)\n", 282 | "\n", 283 | " # Que no se nos olvide retornar los objetos que han de actualizarse\n", 284 | " return line,\n", 285 | "\n", 286 | "# Llamamos a FuncAnimation a\u00f1adiendo nuestra funci\u00f3n inicializadora\n", 287 | "animation = FuncAnimation(fig, animate, init_func=init, frames=3600, interval=100, blit=True)\n", 288 | "\n", 289 | "plt.show() # Representamos" 290 | ], 291 | "language": "python", 292 | "metadata": {}, 293 | "outputs": [] 294 | }, 295 | { 296 | "cell_type": "heading", 297 | "level": 3, 298 | "metadata": {}, 299 | "source": [ 300 | "Oscilador arm\u00f3nico" 301 | ] 302 | }, 303 | { 304 | "cell_type": "markdown", 305 | "metadata": {}, 306 | "source": [ 307 | "Vamos a poner toda la carne en el asador y vamos a representar una part\u00edcula oscilando arm\u00f3nicamente bajo la acci\u00f3n de una fuerza el\u00e1stica lineal. El problema a considerar es:\n", 308 | "\n", 309 | "$$ x = \\sin(x) $$ $$ v = \\cos(x) $$ $$ F = -\\sin(x) $$\n", 310 | "\n", 311 | "Para la part\u00edcula utilizaremos un *patch **Circle*** y para el vector velocidad un *patch **FancyArrowPatch***. La magnitud de la fuerza la representaremos variando el color de la part\u00edcula. Al mismo tiempo, veremos como mostrar texto por la pantalla y c\u00f3mo actualizar el mismo.\n", 312 | "\n", 313 | "El c\u00f3digo es un poco m\u00e1s complejo, pero no nos dejemos abrumar." 314 | ] 315 | }, 316 | { 317 | "cell_type": "code", 318 | "collapsed": false, 319 | "input": [ 320 | "# Creamos una figura, unos ejes, y otros ejes adicionales para la colorbar que utilizaremos para visualizar la intensidad\n", 321 | "# de la fuerza.\n", 322 | "fig, ax = plt.subplots(figsize=(5., 5.))\n", 323 | "ax.set_xlim(-2, 2)\n", 324 | "ax.set_ylim(-2., 2.)\n", 325 | "axb = fig.add_axes([0.215, 0.25, 0.6, 0.05])\n", 326 | "\n", 327 | "# El objeto color map devuelve un color de un determinado degradado, cuando es llamado utilizando un n\u00famero real entre 0 y 1.\n", 328 | "# Como nuestra fuerza est\u00e1 entre esos valores, podemos llamarlo directamente utilizando su intensidad.\n", 329 | "cmap = mpl.cm.jet\n", 330 | "\n", 331 | "# Creamos la colorbar y la asociamos a los ejes expresos para ella.\n", 332 | "cbar = mpl.colorbar.ColorbarBase(axb, cmap=cmap, orientation='horizontal')\n", 333 | "cbar.set_label(r'$|F|$', fontsize=16)\n", 334 | "\n", 335 | "# Creamos nuestro objeto c\u00edrculo y lo asociamos a nuestros ejes.\n", 336 | "c = Circle([0., 0.], 0.5, edgecolor='white')\n", 337 | "ax.add_patch(c)\n", 338 | "\n", 339 | "# Creamos nuestro objeto flecha y lo asociamos a nuestros ejes.\n", 340 | "a = FancyArrowPatch([0., 0.], [0., 0.], linewidth=2, facecolor='red', edgecolor='red')\n", 341 | "a.set_arrowstyle('simple', head_length=6., head_width=2.5)\n", 342 | "ax.add_patch(a)\n", 343 | "\n", 344 | "# Estos objetos mostrar\u00e1n por pantalla los valores de las magnitudes.\n", 345 | "title = ax.text(-0.45, 1.6, '', fontsize=20)\n", 346 | "l1 = ax.text(-1.5, 1.2, '', fontsize=14)\n", 347 | "l2 = ax.text(-1.5, 1., '', fontsize=14)\n", 348 | "l3 = ax.text(0.75, 1.1, '', fontsize=14)\n", 349 | "\n", 350 | "\n", 351 | "def init():\n", 352 | " ''' En la funci\u00f3n inicializadora, como lo los objetos patch no pueden ser inicializados con datos vac\u00edos como las l\u00ednea,.\n", 353 | " lo que hacemos es dibujarlos fuera de los l\u00edmites del gr\u00e1fico.\n", 354 | " '''\n", 355 | " \n", 356 | " c.center = [0., 5.] # Dibujamos el c\u00edrculo fuera del gr\u00e1fico.\n", 357 | " \n", 358 | " a.set_positions([0., 5.], [0., 5.]) # Dibujamos la flecha fuera del gr\u00e1fico.\n", 359 | " \n", 360 | " # Los objetos texto, los inicializamos vac\u00edos.\n", 361 | " title.set_text('')\n", 362 | " l1.set_text('')\n", 363 | " l2.set_text('')\n", 364 | " l3.set_text('')\n", 365 | " \n", 366 | " # Retornamos los objetos a ser actualizados\n", 367 | " return c, a, title, l1, l2, l3\n", 368 | "\n", 369 | "\n", 370 | "def animate(ii):\n", 371 | "\n", 372 | " # Calculamos el valor de las magnitudes.\n", 373 | " t = np.deg2rad((ii * 2) % 360)\n", 374 | " x = np.sin(t)\n", 375 | " v = np.cos(t)\n", 376 | " F = -np.sin(t)\n", 377 | " \n", 378 | " # Actualizamos la posici\u00f3n y color de la part\u00edcula.\n", 379 | " c.center = [x, 0.]\n", 380 | " c.set_color(cmap(np.abs(F)))\n", 381 | " \n", 382 | " # Actualizamos la posici\u00f3n inicial y final de los puntos de la fecha.\n", 383 | " a.set_positions([x, 0.], [v + x, 0.])\n", 384 | " \n", 385 | " # Actualizamos el valor de los objetos que contienen el texto con los valores de las magnitudes.\n", 386 | " title.set_text(r'$t = {0:.2f}$'.format(t))\n", 387 | " l1.set_text(r'$x = {0:.2f}$'.format(x))\n", 388 | " l2.set_text(r'$v = {0:.2f}$'.format(v))\n", 389 | " l3.set_text(r'$F = {0:.2f}$'.format(F))\n", 390 | "\n", 391 | " # Retornamos los objetos a ser actualizados\n", 392 | " return c, a, title, l1, l2, l3\n", 393 | "\n", 394 | "# Llamamos a FuncAnimation\n", 395 | "animation = FuncAnimation(fig, animate, init_func=init, frames=3600, interval=25, blit=True)\n", 396 | "\n", 397 | "plt.show() # Mostramos la animaci\u00f3n" 398 | ], 399 | "language": "python", 400 | "metadata": {}, 401 | "outputs": [] 402 | }, 403 | { 404 | "cell_type": "heading", 405 | "level": 2, 406 | "metadata": {}, 407 | "source": [ 408 | "Guardar las animaciones" 409 | ] 410 | }, 411 | { 412 | "cell_type": "markdown", 413 | "metadata": {}, 414 | "source": [ 415 | "El objeto ***animation*** devuelto por **FuncAnimation** tiene un m\u00e9todo save, que nos permite guardarla en un fichero de v\u00eddeo para compartir con quien queramos, colgarla en internet... para que dicho m\u00e9todo pueda utilizarse es necesario tener uno de estos dos programas: [ffmpeg](http://www.ffmpeg.org/) o ***MEncoder*** instalados y configurados para aparecer en el path del sistema.\n", 416 | "\n", 417 | "Guardar una animaci\u00f3n es tan sencillo como (ejemplo):\n", 418 | "\n", 419 | "*animation* = **FuncAnimation**(...)\n", 420 | "\n", 421 | "*animation*.**save**(*nombre_fichero*, *fps=30*, *writer='ffmpeg'*, *extra_args=['-vcodec', 'libx264']*)\n", 422 | "\n", 423 | "Los argumentos de **save** son:\n", 424 | "\n", 425 | "> **fps**: N\u00famero de cuadros por segundo\n", 426 | "\n", 427 | "> **writer**: Programa que utilizar\u00e1 *Matplotlib* para codificar y generar el fichero de v\u00eddeo.\n", 428 | "\n", 429 | "> **extra_args**: Opci\u00f3n que pasa una lista con par\u00e1metros que el codifcador es capaz de entender. En el ejemplo se usa para que el fichero resultante utilice el c\u00f3dec de alta compresi\u00f3n x264, muy utilizado para colgar v\u00eddeos en internet.\n", 430 | "\n", 431 | "**Nota**: el nombre de fichero ha de incluir la extensi\u00f3n (*nombre_de_fichero='ejemplo.mp4'*)" 432 | ] 433 | }, 434 | { 435 | "cell_type": "heading", 436 | "level": 2, 437 | "metadata": {}, 438 | "source": [ 439 | "Referencias" 440 | ] 441 | }, 442 | { 443 | "cell_type": "markdown", 444 | "metadata": {}, 445 | "source": [ 446 | "> Base de Python reconmendada en el [curso de #aeropython](https://github.com/AeroPython/Curso_AeroPython) de Juan Luis Cano [@Pybonacci](https://twitter.com/Pybonacci) y Alex S\u00e1ez [@Alex_S12](https://twitter.com/Alex__S12)\n", 447 | "\n", 448 | "> Ejemplo de animaci\u00f3n 3D [matplotlib.animation en el blog Pybonacci](https://pybonacci.wordpress.com/tag/animacion/)\n", 449 | "\n", 450 | "> Formas alternativa de generar un fichero de v\u00eddeo en [matplotlib.org](http://matplotlib.org/faq/howto_faq.html?highlight=faq#make-a-movie)\n", 451 | "\n", 452 | "> [Ejemplos de animaciones](http://jakevdp.github.io/blog/2012/08/18/matplotlib-animation-tutorial/) de Jave Vanderplas.\n", 453 | "\n", 454 | "> [Ejemplos de animaciones](http://matplotlib.org/examples/animation/index.html) de *Matplotlib*." 455 | ] 456 | }, 457 | { 458 | "cell_type": "heading", 459 | "level": 2, 460 | "metadata": {}, 461 | "source": [ 462 | "Animaciones del autor" 463 | ] 464 | }, 465 | { 466 | "cell_type": "markdown", 467 | "metadata": {}, 468 | "source": [ 469 | "> [Animaci\u00f3n de Algoritmo Gen\u00e9tico](https://github.com/newlawrence/anigenalg)\n", 470 | "\n", 471 | "> [Animaci\u00f3n de Motor Radial](https://github.com/newlawrence/aniradeng)" 472 | ] 473 | }, 474 | { 475 | "cell_type": "code", 476 | "collapsed": false, 477 | "input": [], 478 | "language": "python", 479 | "metadata": {}, 480 | "outputs": [], 481 | "prompt_number": 5 482 | } 483 | ], 484 | "metadata": {} 485 | } 486 | ] 487 | } -------------------------------------------------------------------------------- /presentacion_curso_2014/reveal.js/css/reveal.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /*! 4 | * reveal.js 5 | * http://lab.hakim.se/reveal-js 6 | * MIT licensed 7 | * 8 | * Copyright (C) 2013 Hakim El Hattab, http://hakim.se 9 | */ 10 | 11 | 12 | /********************************************* 13 | * RESET STYLES 14 | *********************************************/ 15 | 16 | html, body, .reveal div, .reveal span, .reveal applet, .reveal object, .reveal iframe, 17 | .reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6, .reveal p, .reveal blockquote, .reveal pre, 18 | .reveal a, .reveal abbr, .reveal acronym, .reveal address, .reveal big, .reveal cite, .reveal code, 19 | .reveal del, .reveal dfn, .reveal em, .reveal img, .reveal ins, .reveal kbd, .reveal q, .reveal s, .reveal samp, 20 | .reveal small, .reveal strike, .reveal strong, .reveal sub, .reveal sup, .reveal tt, .reveal var, 21 | .reveal b, .reveal u, .reveal i, .reveal center, 22 | .reveal dl, .reveal dt, .reveal dd, .reveal ol, .reveal ul, .reveal li, 23 | .reveal fieldset, .reveal form, .reveal label, .reveal legend, 24 | .reveal table, .reveal caption, .reveal tbody, .reveal tfoot, .reveal thead, .reveal tr, .reveal th, .reveal td, 25 | .reveal article, .reveal aside, .reveal canvas, .reveal details, .reveal embed, 26 | .reveal figure, .reveal figcaption, .reveal footer, .reveal header, .reveal hgroup, 27 | .reveal menu, .reveal nav, .reveal output, .reveal ruby, .reveal section, .reveal summary, 28 | .reveal time, .reveal mark, .reveal audio, video { 29 | margin: 0; 30 | padding: 0; 31 | border: 0; 32 | font-size: 100%; 33 | font: inherit; 34 | vertical-align: baseline; 35 | } 36 | 37 | .reveal article, .reveal aside, .reveal details, .reveal figcaption, .reveal figure, 38 | .reveal footer, .reveal header, .reveal hgroup, .reveal menu, .reveal nav, .reveal section { 39 | display: block; 40 | } 41 | 42 | 43 | /********************************************* 44 | * GLOBAL STYLES 45 | *********************************************/ 46 | 47 | html, 48 | body { 49 | width: 100%; 50 | height: 100%; 51 | overflow: hidden; 52 | } 53 | 54 | body { 55 | position: relative; 56 | line-height: 1; 57 | } 58 | 59 | ::selection { 60 | background: #FF5E99; 61 | color: #fff; 62 | text-shadow: none; 63 | } 64 | 65 | 66 | /********************************************* 67 | * HEADERS 68 | *********************************************/ 69 | 70 | .reveal h1, 71 | .reveal h2, 72 | .reveal h3, 73 | .reveal h4, 74 | .reveal h5, 75 | .reveal h6 { 76 | -webkit-hyphens: auto; 77 | -moz-hyphens: auto; 78 | hyphens: auto; 79 | 80 | word-wrap: break-word; 81 | line-height: 1; 82 | } 83 | 84 | .reveal h1 { font-size: 3.77em; } 85 | .reveal h2 { font-size: 2.11em; } 86 | .reveal h3 { font-size: 1.55em; } 87 | .reveal h4 { font-size: 1em; } 88 | 89 | 90 | /********************************************* 91 | * VIEW FRAGMENTS 92 | *********************************************/ 93 | 94 | .reveal .slides section .fragment { 95 | opacity: 0; 96 | 97 | -webkit-transition: all .2s ease; 98 | -moz-transition: all .2s ease; 99 | -ms-transition: all .2s ease; 100 | -o-transition: all .2s ease; 101 | transition: all .2s ease; 102 | } 103 | .reveal .slides section .fragment.visible { 104 | opacity: 1; 105 | } 106 | 107 | .reveal .slides section .fragment.grow { 108 | opacity: 1; 109 | } 110 | .reveal .slides section .fragment.grow.visible { 111 | -webkit-transform: scale( 1.3 ); 112 | -moz-transform: scale( 1.3 ); 113 | -ms-transform: scale( 1.3 ); 114 | -o-transform: scale( 1.3 ); 115 | transform: scale( 1.3 ); 116 | } 117 | 118 | .reveal .slides section .fragment.shrink { 119 | opacity: 1; 120 | } 121 | .reveal .slides section .fragment.shrink.visible { 122 | -webkit-transform: scale( 0.7 ); 123 | -moz-transform: scale( 0.7 ); 124 | -ms-transform: scale( 0.7 ); 125 | -o-transform: scale( 0.7 ); 126 | transform: scale( 0.7 ); 127 | } 128 | 129 | .reveal .slides section .fragment.zoom-in { 130 | opacity: 0; 131 | 132 | -webkit-transform: scale( 0.1 ); 133 | -moz-transform: scale( 0.1 ); 134 | -ms-transform: scale( 0.1 ); 135 | -o-transform: scale( 0.1 ); 136 | transform: scale( 0.1 ); 137 | } 138 | 139 | .reveal .slides section .fragment.zoom-in.visible { 140 | opacity: 1; 141 | 142 | -webkit-transform: scale( 1 ); 143 | -moz-transform: scale( 1 ); 144 | -ms-transform: scale( 1 ); 145 | -o-transform: scale( 1 ); 146 | transform: scale( 1 ); 147 | } 148 | 149 | .reveal .slides section .fragment.roll-in { 150 | opacity: 0; 151 | 152 | -webkit-transform: rotateX( 90deg ); 153 | -moz-transform: rotateX( 90deg ); 154 | -ms-transform: rotateX( 90deg ); 155 | -o-transform: rotateX( 90deg ); 156 | transform: rotateX( 90deg ); 157 | } 158 | .reveal .slides section .fragment.roll-in.visible { 159 | opacity: 1; 160 | 161 | -webkit-transform: rotateX( 0 ); 162 | -moz-transform: rotateX( 0 ); 163 | -ms-transform: rotateX( 0 ); 164 | -o-transform: rotateX( 0 ); 165 | transform: rotateX( 0 ); 166 | } 167 | 168 | .reveal .slides section .fragment.fade-out { 169 | opacity: 1; 170 | } 171 | .reveal .slides section .fragment.fade-out.visible { 172 | opacity: 0; 173 | } 174 | 175 | .reveal .slides section .fragment.semi-fade-out { 176 | opacity: 1; 177 | } 178 | .reveal .slides section .fragment.semi-fade-out.visible { 179 | opacity: 0.5; 180 | } 181 | 182 | .reveal .slides section .fragment.highlight-red, 183 | .reveal .slides section .fragment.highlight-green, 184 | .reveal .slides section .fragment.highlight-blue { 185 | opacity: 1; 186 | } 187 | .reveal .slides section .fragment.highlight-red.visible { 188 | color: #ff2c2d 189 | } 190 | .reveal .slides section .fragment.highlight-green.visible { 191 | color: #17ff2e; 192 | } 193 | .reveal .slides section .fragment.highlight-blue.visible { 194 | color: #1b91ff; 195 | } 196 | 197 | 198 | /********************************************* 199 | * DEFAULT ELEMENT STYLES 200 | *********************************************/ 201 | 202 | /* Fixes issue in Chrome where italic fonts did not appear when printing to PDF */ 203 | .reveal:after { 204 | content: ''; 205 | font-style: italic; 206 | } 207 | 208 | .reveal iframe { 209 | z-index: 1; 210 | } 211 | 212 | /* Ensure certain elements are never larger than the slide itself */ 213 | .reveal img, 214 | .reveal video, 215 | .reveal iframe { 216 | max-width: 95%; 217 | max-height: 95%; 218 | } 219 | 220 | /** Prevents layering issues in certain browser/transition combinations */ 221 | .reveal a { 222 | position: relative; 223 | } 224 | 225 | .reveal strong, 226 | .reveal b { 227 | font-weight: bold; 228 | } 229 | 230 | .reveal em, 231 | .reveal i { 232 | font-style: italic; 233 | } 234 | 235 | .reveal ol, 236 | .reveal ul { 237 | display: inline-block; 238 | 239 | text-align: left; 240 | margin: 0 0 0 1em; 241 | } 242 | 243 | .reveal ol { 244 | list-style-type: decimal; 245 | } 246 | 247 | .reveal ul { 248 | list-style-type: disc; 249 | } 250 | 251 | .reveal ul ul { 252 | list-style-type: square; 253 | } 254 | 255 | .reveal ul ul ul { 256 | list-style-type: circle; 257 | } 258 | 259 | .reveal ul ul, 260 | .reveal ul ol, 261 | .reveal ol ol, 262 | .reveal ol ul { 263 | display: block; 264 | margin-left: 40px; 265 | } 266 | 267 | .reveal p { 268 | margin-bottom: 10px; 269 | line-height: 1.2em; 270 | } 271 | 272 | .reveal q, 273 | .reveal blockquote { 274 | quotes: none; 275 | } 276 | 277 | .reveal blockquote { 278 | display: block; 279 | position: relative; 280 | width: 70%; 281 | margin: 5px auto; 282 | padding: 5px; 283 | 284 | font-style: italic; 285 | background: rgba(255, 255, 255, 0.05); 286 | box-shadow: 0px 0px 2px rgba(0,0,0,0.2); 287 | } 288 | .reveal blockquote p:first-child, 289 | .reveal blockquote p:last-child { 290 | display: inline-block; 291 | } 292 | 293 | .reveal q { 294 | font-style: italic; 295 | } 296 | 297 | .reveal pre { 298 | display: block; 299 | position: relative; 300 | width: 90%; 301 | margin: 15px auto; 302 | 303 | text-align: left; 304 | font-size: 0.55em; 305 | font-family: monospace; 306 | line-height: 1.2em; 307 | 308 | word-wrap: break-word; 309 | 310 | box-shadow: 0px 0px 6px rgba(0,0,0,0.3); 311 | } 312 | .reveal code { 313 | font-family: monospace; 314 | } 315 | .reveal pre code { 316 | padding: 5px; 317 | overflow: auto; 318 | max-height: 400px; 319 | word-wrap: normal; 320 | } 321 | .reveal pre.stretch code { 322 | height: 100%; 323 | max-height: 100%; 324 | 325 | -webkit-box-sizing: border-box; 326 | -moz-box-sizing: border-box; 327 | box-sizing: border-box; 328 | } 329 | 330 | .reveal table th, 331 | .reveal table td { 332 | text-align: left; 333 | padding-right: .3em; 334 | } 335 | 336 | .reveal table th { 337 | text-shadow: rgb(255,255,255) 1px 1px 2px; 338 | } 339 | 340 | .reveal sup { 341 | vertical-align: super; 342 | } 343 | .reveal sub { 344 | vertical-align: sub; 345 | } 346 | 347 | .reveal small { 348 | display: inline-block; 349 | font-size: 0.6em; 350 | line-height: 1.2em; 351 | vertical-align: top; 352 | } 353 | 354 | .reveal small * { 355 | vertical-align: top; 356 | } 357 | 358 | .reveal .stretch { 359 | max-width: none; 360 | max-height: none; 361 | } 362 | 363 | 364 | /********************************************* 365 | * CONTROLS 366 | *********************************************/ 367 | 368 | .reveal .controls { 369 | display: none; 370 | position: fixed; 371 | width: 110px; 372 | height: 110px; 373 | z-index: 30; 374 | right: 10px; 375 | bottom: 10px; 376 | } 377 | 378 | .reveal .controls div { 379 | position: absolute; 380 | opacity: 0.05; 381 | width: 0; 382 | height: 0; 383 | border: 12px solid transparent; 384 | 385 | -moz-transform: scale(.9999); 386 | 387 | -webkit-transition: all 0.2s ease; 388 | -moz-transition: all 0.2s ease; 389 | -ms-transition: all 0.2s ease; 390 | -o-transition: all 0.2s ease; 391 | transition: all 0.2s ease; 392 | } 393 | 394 | .reveal .controls div.enabled { 395 | opacity: 0.7; 396 | cursor: pointer; 397 | } 398 | 399 | .reveal .controls div.enabled:active { 400 | margin-top: 1px; 401 | } 402 | 403 | .reveal .controls div.navigate-left { 404 | top: 42px; 405 | 406 | border-right-width: 22px; 407 | border-right-color: #eee; 408 | } 409 | .reveal .controls div.navigate-left.fragmented { 410 | opacity: 0.3; 411 | } 412 | 413 | .reveal .controls div.navigate-right { 414 | left: 74px; 415 | top: 42px; 416 | 417 | border-left-width: 22px; 418 | border-left-color: #eee; 419 | } 420 | .reveal .controls div.navigate-right.fragmented { 421 | opacity: 0.3; 422 | } 423 | 424 | .reveal .controls div.navigate-up { 425 | left: 42px; 426 | 427 | border-bottom-width: 22px; 428 | border-bottom-color: #eee; 429 | } 430 | .reveal .controls div.navigate-up.fragmented { 431 | opacity: 0.3; 432 | } 433 | 434 | .reveal .controls div.navigate-down { 435 | left: 42px; 436 | top: 74px; 437 | 438 | border-top-width: 22px; 439 | border-top-color: #eee; 440 | } 441 | .reveal .controls div.navigate-down.fragmented { 442 | opacity: 0.3; 443 | } 444 | 445 | 446 | /********************************************* 447 | * PROGRESS BAR 448 | *********************************************/ 449 | 450 | .reveal .progress { 451 | position: fixed; 452 | display: none; 453 | height: 3px; 454 | width: 100%; 455 | bottom: 0; 456 | left: 0; 457 | z-index: 10; 458 | } 459 | .reveal .progress:after { 460 | content: ''; 461 | display: 'block'; 462 | position: absolute; 463 | height: 20px; 464 | width: 100%; 465 | top: -20px; 466 | } 467 | .reveal .progress span { 468 | display: block; 469 | height: 100%; 470 | width: 0px; 471 | 472 | -webkit-transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); 473 | -moz-transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); 474 | -ms-transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); 475 | -o-transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); 476 | transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); 477 | } 478 | 479 | 480 | /********************************************* 481 | * SLIDES 482 | *********************************************/ 483 | 484 | .reveal { 485 | position: relative; 486 | width: 100%; 487 | height: 100%; 488 | 489 | -ms-touch-action: none; 490 | } 491 | 492 | .reveal .slides { 493 | position: absolute; 494 | width: 100%; 495 | height: 100%; 496 | left: 50%; 497 | top: 50%; 498 | 499 | overflow: visible; 500 | z-index: 1; 501 | text-align: center; 502 | 503 | -webkit-transition: -webkit-perspective .4s ease; 504 | -moz-transition: -moz-perspective .4s ease; 505 | -ms-transition: -ms-perspective .4s ease; 506 | -o-transition: -o-perspective .4s ease; 507 | transition: perspective .4s ease; 508 | 509 | -webkit-perspective: 600px; 510 | -moz-perspective: 600px; 511 | -ms-perspective: 600px; 512 | perspective: 600px; 513 | 514 | -webkit-perspective-origin: 0px -100px; 515 | -moz-perspective-origin: 0px -100px; 516 | -ms-perspective-origin: 0px -100px; 517 | perspective-origin: 0px -100px; 518 | } 519 | 520 | .reveal .slides>section, 521 | .reveal .slides>section>section { 522 | display: none; 523 | position: absolute; 524 | width: 100%; 525 | padding: 20px 0px; 526 | 527 | z-index: 10; 528 | line-height: 1.2em; 529 | font-weight: normal; 530 | 531 | -webkit-transform-style: preserve-3d; 532 | -moz-transform-style: preserve-3d; 533 | -ms-transform-style: preserve-3d; 534 | transform-style: preserve-3d; 535 | 536 | -webkit-transition: -webkit-transform-origin 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985), 537 | -webkit-transform 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985), 538 | visibility 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985), 539 | opacity 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); 540 | -moz-transition: -moz-transform-origin 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985), 541 | -moz-transform 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985), 542 | visibility 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985), 543 | opacity 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); 544 | -ms-transition: -ms-transform-origin 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985), 545 | -ms-transform 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985), 546 | visibility 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985), 547 | opacity 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); 548 | -o-transition: -o-transform-origin 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985), 549 | -o-transform 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985), 550 | visibility 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985), 551 | opacity 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); 552 | transition: transform-origin 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985), 553 | transform 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985), 554 | visibility 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985), 555 | opacity 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); 556 | } 557 | 558 | /* Global transition speed settings */ 559 | .reveal[data-transition-speed="fast"] .slides section { 560 | -webkit-transition-duration: 400ms; 561 | -moz-transition-duration: 400ms; 562 | -ms-transition-duration: 400ms; 563 | transition-duration: 400ms; 564 | } 565 | .reveal[data-transition-speed="slow"] .slides section { 566 | -webkit-transition-duration: 1200ms; 567 | -moz-transition-duration: 1200ms; 568 | -ms-transition-duration: 1200ms; 569 | transition-duration: 1200ms; 570 | } 571 | 572 | /* Slide-specific transition speed overrides */ 573 | .reveal .slides section[data-transition-speed="fast"] { 574 | -webkit-transition-duration: 400ms; 575 | -moz-transition-duration: 400ms; 576 | -ms-transition-duration: 400ms; 577 | transition-duration: 400ms; 578 | } 579 | .reveal .slides section[data-transition-speed="slow"] { 580 | -webkit-transition-duration: 1200ms; 581 | -moz-transition-duration: 1200ms; 582 | -ms-transition-duration: 1200ms; 583 | transition-duration: 1200ms; 584 | } 585 | 586 | .reveal .slides>section { 587 | left: -50%; 588 | top: -50%; 589 | } 590 | 591 | .reveal .slides>section.stack { 592 | padding-top: 0; 593 | padding-bottom: 0; 594 | } 595 | 596 | .reveal .slides>section.present, 597 | .reveal .slides>section>section.present { 598 | display: block; 599 | z-index: 11; 600 | opacity: 1; 601 | } 602 | 603 | .reveal.center, 604 | .reveal.center .slides, 605 | .reveal.center .slides section { 606 | min-height: auto !important; 607 | } 608 | 609 | /* Don't allow interaction with invisible slides */ 610 | .reveal .slides>section.future, 611 | .reveal .slides>section>section.future, 612 | .reveal .slides>section.past, 613 | .reveal .slides>section>section.past { 614 | pointer-events: none; 615 | } 616 | 617 | .reveal.overview .slides>section, 618 | .reveal.overview .slides>section>section { 619 | pointer-events: auto; 620 | } 621 | 622 | 623 | 624 | /********************************************* 625 | * DEFAULT TRANSITION 626 | *********************************************/ 627 | 628 | .reveal .slides>section[data-transition=default].past, 629 | .reveal .slides>section.past { 630 | display: block; 631 | opacity: 0; 632 | 633 | -webkit-transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); 634 | -moz-transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); 635 | -ms-transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); 636 | transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); 637 | } 638 | .reveal .slides>section[data-transition=default].future, 639 | .reveal .slides>section.future { 640 | display: block; 641 | opacity: 0; 642 | 643 | -webkit-transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0); 644 | -moz-transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0); 645 | -ms-transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0); 646 | transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0); 647 | } 648 | 649 | .reveal .slides>section>section[data-transition=default].past, 650 | .reveal .slides>section>section.past { 651 | display: block; 652 | opacity: 0; 653 | 654 | -webkit-transform: translate3d(0, -300px, 0) rotateX(70deg) translate3d(0, -300px, 0); 655 | -moz-transform: translate3d(0, -300px, 0) rotateX(70deg) translate3d(0, -300px, 0); 656 | -ms-transform: translate3d(0, -300px, 0) rotateX(70deg) translate3d(0, -300px, 0); 657 | transform: translate3d(0, -300px, 0) rotateX(70deg) translate3d(0, -300px, 0); 658 | } 659 | .reveal .slides>section>section[data-transition=default].future, 660 | .reveal .slides>section>section.future { 661 | display: block; 662 | opacity: 0; 663 | 664 | -webkit-transform: translate3d(0, 300px, 0) rotateX(-70deg) translate3d(0, 300px, 0); 665 | -moz-transform: translate3d(0, 300px, 0) rotateX(-70deg) translate3d(0, 300px, 0); 666 | -ms-transform: translate3d(0, 300px, 0) rotateX(-70deg) translate3d(0, 300px, 0); 667 | transform: translate3d(0, 300px, 0) rotateX(-70deg) translate3d(0, 300px, 0); 668 | } 669 | 670 | 671 | /********************************************* 672 | * CONCAVE TRANSITION 673 | *********************************************/ 674 | 675 | .reveal .slides>section[data-transition=concave].past, 676 | .reveal.concave .slides>section.past { 677 | -webkit-transform: translate3d(-100%, 0, 0) rotateY(90deg) translate3d(-100%, 0, 0); 678 | -moz-transform: translate3d(-100%, 0, 0) rotateY(90deg) translate3d(-100%, 0, 0); 679 | -ms-transform: translate3d(-100%, 0, 0) rotateY(90deg) translate3d(-100%, 0, 0); 680 | transform: translate3d(-100%, 0, 0) rotateY(90deg) translate3d(-100%, 0, 0); 681 | } 682 | .reveal .slides>section[data-transition=concave].future, 683 | .reveal.concave .slides>section.future { 684 | -webkit-transform: translate3d(100%, 0, 0) rotateY(-90deg) translate3d(100%, 0, 0); 685 | -moz-transform: translate3d(100%, 0, 0) rotateY(-90deg) translate3d(100%, 0, 0); 686 | -ms-transform: translate3d(100%, 0, 0) rotateY(-90deg) translate3d(100%, 0, 0); 687 | transform: translate3d(100%, 0, 0) rotateY(-90deg) translate3d(100%, 0, 0); 688 | } 689 | 690 | .reveal .slides>section>section[data-transition=concave].past, 691 | .reveal.concave .slides>section>section.past { 692 | -webkit-transform: translate3d(0, -80%, 0) rotateX(-70deg) translate3d(0, -80%, 0); 693 | -moz-transform: translate3d(0, -80%, 0) rotateX(-70deg) translate3d(0, -80%, 0); 694 | -ms-transform: translate3d(0, -80%, 0) rotateX(-70deg) translate3d(0, -80%, 0); 695 | transform: translate3d(0, -80%, 0) rotateX(-70deg) translate3d(0, -80%, 0); 696 | } 697 | .reveal .slides>section>section[data-transition=concave].future, 698 | .reveal.concave .slides>section>section.future { 699 | -webkit-transform: translate3d(0, 80%, 0) rotateX(70deg) translate3d(0, 80%, 0); 700 | -moz-transform: translate3d(0, 80%, 0) rotateX(70deg) translate3d(0, 80%, 0); 701 | -ms-transform: translate3d(0, 80%, 0) rotateX(70deg) translate3d(0, 80%, 0); 702 | transform: translate3d(0, 80%, 0) rotateX(70deg) translate3d(0, 80%, 0); 703 | } 704 | 705 | 706 | /********************************************* 707 | * ZOOM TRANSITION 708 | *********************************************/ 709 | 710 | .reveal .slides>section[data-transition=zoom].past, 711 | .reveal.zoom .slides>section.past { 712 | opacity: 0; 713 | visibility: hidden; 714 | 715 | -webkit-transform: scale(16); 716 | -moz-transform: scale(16); 717 | -ms-transform: scale(16); 718 | -o-transform: scale(16); 719 | transform: scale(16); 720 | } 721 | .reveal .slides>section[data-transition=zoom].future, 722 | .reveal.zoom .slides>section.future { 723 | opacity: 0; 724 | visibility: hidden; 725 | 726 | -webkit-transform: scale(0.2); 727 | -moz-transform: scale(0.2); 728 | -ms-transform: scale(0.2); 729 | -o-transform: scale(0.2); 730 | transform: scale(0.2); 731 | } 732 | 733 | .reveal .slides>section>section[data-transition=zoom].past, 734 | .reveal.zoom .slides>section>section.past { 735 | -webkit-transform: translate(0, -150%); 736 | -moz-transform: translate(0, -150%); 737 | -ms-transform: translate(0, -150%); 738 | -o-transform: translate(0, -150%); 739 | transform: translate(0, -150%); 740 | } 741 | .reveal .slides>section>section[data-transition=zoom].future, 742 | .reveal.zoom .slides>section>section.future { 743 | -webkit-transform: translate(0, 150%); 744 | -moz-transform: translate(0, 150%); 745 | -ms-transform: translate(0, 150%); 746 | -o-transform: translate(0, 150%); 747 | transform: translate(0, 150%); 748 | } 749 | 750 | 751 | /********************************************* 752 | * LINEAR TRANSITION 753 | *********************************************/ 754 | 755 | .reveal.linear section { 756 | -webkit-backface-visibility: hidden; 757 | -moz-backface-visibility: hidden; 758 | -ms-backface-visibility: hidden; 759 | backface-visibility: hidden; 760 | } 761 | 762 | .reveal .slides>section[data-transition=linear].past, 763 | .reveal.linear .slides>section.past { 764 | -webkit-transform: translate(-150%, 0); 765 | -moz-transform: translate(-150%, 0); 766 | -ms-transform: translate(-150%, 0); 767 | -o-transform: translate(-150%, 0); 768 | transform: translate(-150%, 0); 769 | } 770 | .reveal .slides>section[data-transition=linear].future, 771 | .reveal.linear .slides>section.future { 772 | -webkit-transform: translate(150%, 0); 773 | -moz-transform: translate(150%, 0); 774 | -ms-transform: translate(150%, 0); 775 | -o-transform: translate(150%, 0); 776 | transform: translate(150%, 0); 777 | } 778 | 779 | .reveal .slides>section>section[data-transition=linear].past, 780 | .reveal.linear .slides>section>section.past { 781 | -webkit-transform: translate(0, -150%); 782 | -moz-transform: translate(0, -150%); 783 | -ms-transform: translate(0, -150%); 784 | -o-transform: translate(0, -150%); 785 | transform: translate(0, -150%); 786 | } 787 | .reveal .slides>section>section[data-transition=linear].future, 788 | .reveal.linear .slides>section>section.future { 789 | -webkit-transform: translate(0, 150%); 790 | -moz-transform: translate(0, 150%); 791 | -ms-transform: translate(0, 150%); 792 | -o-transform: translate(0, 150%); 793 | transform: translate(0, 150%); 794 | } 795 | 796 | 797 | /********************************************* 798 | * CUBE TRANSITION 799 | *********************************************/ 800 | 801 | .reveal.cube .slides { 802 | -webkit-perspective: 1300px; 803 | -moz-perspective: 1300px; 804 | -ms-perspective: 1300px; 805 | perspective: 1300px; 806 | } 807 | 808 | .reveal.cube .slides section { 809 | padding: 30px; 810 | min-height: 700px; 811 | 812 | -webkit-backface-visibility: hidden; 813 | -moz-backface-visibility: hidden; 814 | -ms-backface-visibility: hidden; 815 | backface-visibility: hidden; 816 | 817 | -webkit-box-sizing: border-box; 818 | -moz-box-sizing: border-box; 819 | box-sizing: border-box; 820 | } 821 | .reveal.center.cube .slides section { 822 | min-height: auto; 823 | } 824 | .reveal.cube .slides section:not(.stack):before { 825 | content: ''; 826 | position: absolute; 827 | display: block; 828 | width: 100%; 829 | height: 100%; 830 | left: 0; 831 | top: 0; 832 | background: rgba(0,0,0,0.1); 833 | border-radius: 4px; 834 | 835 | -webkit-transform: translateZ( -20px ); 836 | -moz-transform: translateZ( -20px ); 837 | -ms-transform: translateZ( -20px ); 838 | -o-transform: translateZ( -20px ); 839 | transform: translateZ( -20px ); 840 | } 841 | .reveal.cube .slides section:not(.stack):after { 842 | content: ''; 843 | position: absolute; 844 | display: block; 845 | width: 90%; 846 | height: 30px; 847 | left: 5%; 848 | bottom: 0; 849 | background: none; 850 | z-index: 1; 851 | 852 | border-radius: 4px; 853 | box-shadow: 0px 95px 25px rgba(0,0,0,0.2); 854 | 855 | -webkit-transform: translateZ(-90px) rotateX( 65deg ); 856 | -moz-transform: translateZ(-90px) rotateX( 65deg ); 857 | -ms-transform: translateZ(-90px) rotateX( 65deg ); 858 | -o-transform: translateZ(-90px) rotateX( 65deg ); 859 | transform: translateZ(-90px) rotateX( 65deg ); 860 | } 861 | 862 | .reveal.cube .slides>section.stack { 863 | padding: 0; 864 | background: none; 865 | } 866 | 867 | .reveal.cube .slides>section.past { 868 | -webkit-transform-origin: 100% 0%; 869 | -moz-transform-origin: 100% 0%; 870 | -ms-transform-origin: 100% 0%; 871 | transform-origin: 100% 0%; 872 | 873 | -webkit-transform: translate3d(-100%, 0, 0) rotateY(-90deg); 874 | -moz-transform: translate3d(-100%, 0, 0) rotateY(-90deg); 875 | -ms-transform: translate3d(-100%, 0, 0) rotateY(-90deg); 876 | transform: translate3d(-100%, 0, 0) rotateY(-90deg); 877 | } 878 | 879 | .reveal.cube .slides>section.future { 880 | -webkit-transform-origin: 0% 0%; 881 | -moz-transform-origin: 0% 0%; 882 | -ms-transform-origin: 0% 0%; 883 | transform-origin: 0% 0%; 884 | 885 | -webkit-transform: translate3d(100%, 0, 0) rotateY(90deg); 886 | -moz-transform: translate3d(100%, 0, 0) rotateY(90deg); 887 | -ms-transform: translate3d(100%, 0, 0) rotateY(90deg); 888 | transform: translate3d(100%, 0, 0) rotateY(90deg); 889 | } 890 | 891 | .reveal.cube .slides>section>section.past { 892 | -webkit-transform-origin: 0% 100%; 893 | -moz-transform-origin: 0% 100%; 894 | -ms-transform-origin: 0% 100%; 895 | transform-origin: 0% 100%; 896 | 897 | -webkit-transform: translate3d(0, -100%, 0) rotateX(90deg); 898 | -moz-transform: translate3d(0, -100%, 0) rotateX(90deg); 899 | -ms-transform: translate3d(0, -100%, 0) rotateX(90deg); 900 | transform: translate3d(0, -100%, 0) rotateX(90deg); 901 | } 902 | 903 | .reveal.cube .slides>section>section.future { 904 | -webkit-transform-origin: 0% 0%; 905 | -moz-transform-origin: 0% 0%; 906 | -ms-transform-origin: 0% 0%; 907 | transform-origin: 0% 0%; 908 | 909 | -webkit-transform: translate3d(0, 100%, 0) rotateX(-90deg); 910 | -moz-transform: translate3d(0, 100%, 0) rotateX(-90deg); 911 | -ms-transform: translate3d(0, 100%, 0) rotateX(-90deg); 912 | transform: translate3d(0, 100%, 0) rotateX(-90deg); 913 | } 914 | 915 | 916 | /********************************************* 917 | * PAGE TRANSITION 918 | *********************************************/ 919 | 920 | .reveal.page .slides { 921 | -webkit-perspective-origin: 0% 50%; 922 | -moz-perspective-origin: 0% 50%; 923 | -ms-perspective-origin: 0% 50%; 924 | perspective-origin: 0% 50%; 925 | 926 | -webkit-perspective: 3000px; 927 | -moz-perspective: 3000px; 928 | -ms-perspective: 3000px; 929 | perspective: 3000px; 930 | } 931 | 932 | .reveal.page .slides section { 933 | padding: 30px; 934 | min-height: 700px; 935 | 936 | -webkit-box-sizing: border-box; 937 | -moz-box-sizing: border-box; 938 | box-sizing: border-box; 939 | } 940 | .reveal.page .slides section.past { 941 | z-index: 12; 942 | } 943 | .reveal.page .slides section:not(.stack):before { 944 | content: ''; 945 | position: absolute; 946 | display: block; 947 | width: 100%; 948 | height: 100%; 949 | left: 0; 950 | top: 0; 951 | background: rgba(0,0,0,0.1); 952 | 953 | -webkit-transform: translateZ( -20px ); 954 | -moz-transform: translateZ( -20px ); 955 | -ms-transform: translateZ( -20px ); 956 | -o-transform: translateZ( -20px ); 957 | transform: translateZ( -20px ); 958 | } 959 | .reveal.page .slides section:not(.stack):after { 960 | content: ''; 961 | position: absolute; 962 | display: block; 963 | width: 90%; 964 | height: 30px; 965 | left: 5%; 966 | bottom: 0; 967 | background: none; 968 | z-index: 1; 969 | 970 | border-radius: 4px; 971 | box-shadow: 0px 95px 25px rgba(0,0,0,0.2); 972 | 973 | -webkit-transform: translateZ(-90px) rotateX( 65deg ); 974 | } 975 | 976 | .reveal.page .slides>section.stack { 977 | padding: 0; 978 | background: none; 979 | } 980 | 981 | .reveal.page .slides>section.past { 982 | -webkit-transform-origin: 0% 0%; 983 | -moz-transform-origin: 0% 0%; 984 | -ms-transform-origin: 0% 0%; 985 | transform-origin: 0% 0%; 986 | 987 | -webkit-transform: translate3d(-40%, 0, 0) rotateY(-80deg); 988 | -moz-transform: translate3d(-40%, 0, 0) rotateY(-80deg); 989 | -ms-transform: translate3d(-40%, 0, 0) rotateY(-80deg); 990 | transform: translate3d(-40%, 0, 0) rotateY(-80deg); 991 | } 992 | 993 | .reveal.page .slides>section.future { 994 | -webkit-transform-origin: 100% 0%; 995 | -moz-transform-origin: 100% 0%; 996 | -ms-transform-origin: 100% 0%; 997 | transform-origin: 100% 0%; 998 | 999 | -webkit-transform: translate3d(0, 0, 0); 1000 | -moz-transform: translate3d(0, 0, 0); 1001 | -ms-transform: translate3d(0, 0, 0); 1002 | transform: translate3d(0, 0, 0); 1003 | } 1004 | 1005 | .reveal.page .slides>section>section.past { 1006 | -webkit-transform-origin: 0% 0%; 1007 | -moz-transform-origin: 0% 0%; 1008 | -ms-transform-origin: 0% 0%; 1009 | transform-origin: 0% 0%; 1010 | 1011 | -webkit-transform: translate3d(0, -40%, 0) rotateX(80deg); 1012 | -moz-transform: translate3d(0, -40%, 0) rotateX(80deg); 1013 | -ms-transform: translate3d(0, -40%, 0) rotateX(80deg); 1014 | transform: translate3d(0, -40%, 0) rotateX(80deg); 1015 | } 1016 | 1017 | .reveal.page .slides>section>section.future { 1018 | -webkit-transform-origin: 0% 100%; 1019 | -moz-transform-origin: 0% 100%; 1020 | -ms-transform-origin: 0% 100%; 1021 | transform-origin: 0% 100%; 1022 | 1023 | -webkit-transform: translate3d(0, 0, 0); 1024 | -moz-transform: translate3d(0, 0, 0); 1025 | -ms-transform: translate3d(0, 0, 0); 1026 | transform: translate3d(0, 0, 0); 1027 | } 1028 | 1029 | 1030 | /********************************************* 1031 | * FADE TRANSITION 1032 | *********************************************/ 1033 | 1034 | .reveal .slides section[data-transition=fade], 1035 | .reveal.fade .slides section, 1036 | .reveal.fade .slides>section>section { 1037 | -webkit-transform: none; 1038 | -moz-transform: none; 1039 | -ms-transform: none; 1040 | -o-transform: none; 1041 | transform: none; 1042 | 1043 | -webkit-transition: opacity 0.5s; 1044 | -moz-transition: opacity 0.5s; 1045 | -ms-transition: opacity 0.5s; 1046 | -o-transition: opacity 0.5s; 1047 | transition: opacity 0.5s; 1048 | } 1049 | 1050 | 1051 | .reveal.fade.overview .slides section, 1052 | .reveal.fade.overview .slides>section>section, 1053 | .reveal.fade.exit-overview .slides section, 1054 | .reveal.fade.exit-overview .slides>section>section { 1055 | -webkit-transition: none; 1056 | -moz-transition: none; 1057 | -ms-transition: none; 1058 | -o-transition: none; 1059 | transition: none; 1060 | } 1061 | 1062 | 1063 | /********************************************* 1064 | * NO TRANSITION 1065 | *********************************************/ 1066 | 1067 | .reveal .slides section[data-transition=none], 1068 | .reveal.none .slides section { 1069 | -webkit-transform: none; 1070 | -moz-transform: none; 1071 | -ms-transform: none; 1072 | -o-transform: none; 1073 | transform: none; 1074 | 1075 | -webkit-transition: none; 1076 | -moz-transition: none; 1077 | -ms-transition: none; 1078 | -o-transition: none; 1079 | transition: none; 1080 | } 1081 | 1082 | 1083 | /********************************************* 1084 | * OVERVIEW 1085 | *********************************************/ 1086 | 1087 | .reveal.overview .slides { 1088 | -webkit-perspective-origin: 0% 0%; 1089 | -moz-perspective-origin: 0% 0%; 1090 | -ms-perspective-origin: 0% 0%; 1091 | perspective-origin: 0% 0%; 1092 | 1093 | -webkit-perspective: 700px; 1094 | -moz-perspective: 700px; 1095 | -ms-perspective: 700px; 1096 | perspective: 700px; 1097 | } 1098 | 1099 | .reveal.overview .slides section { 1100 | height: 600px; 1101 | top: -300px !important; 1102 | overflow: hidden; 1103 | opacity: 1 !important; 1104 | visibility: visible !important; 1105 | cursor: pointer; 1106 | background: rgba(0,0,0,0.1); 1107 | } 1108 | .reveal.overview .slides section .fragment { 1109 | opacity: 1; 1110 | } 1111 | .reveal.overview .slides section:after, 1112 | .reveal.overview .slides section:before { 1113 | display: none !important; 1114 | } 1115 | .reveal.overview .slides section>section { 1116 | opacity: 1; 1117 | cursor: pointer; 1118 | } 1119 | .reveal.overview .slides section:hover { 1120 | background: rgba(0,0,0,0.3); 1121 | } 1122 | .reveal.overview .slides section.present { 1123 | background: rgba(0,0,0,0.3); 1124 | } 1125 | .reveal.overview .slides>section.stack { 1126 | padding: 0; 1127 | top: 0 !important; 1128 | background: none; 1129 | overflow: visible; 1130 | } 1131 | 1132 | 1133 | /********************************************* 1134 | * PAUSED MODE 1135 | *********************************************/ 1136 | 1137 | .reveal .pause-overlay { 1138 | position: absolute; 1139 | top: 0; 1140 | left: 0; 1141 | width: 100%; 1142 | height: 100%; 1143 | background: black; 1144 | visibility: hidden; 1145 | opacity: 0; 1146 | z-index: 100; 1147 | 1148 | -webkit-transition: all 1s ease; 1149 | -moz-transition: all 1s ease; 1150 | -ms-transition: all 1s ease; 1151 | -o-transition: all 1s ease; 1152 | transition: all 1s ease; 1153 | } 1154 | .reveal.paused .pause-overlay { 1155 | visibility: visible; 1156 | opacity: 1; 1157 | } 1158 | 1159 | 1160 | /********************************************* 1161 | * FALLBACK 1162 | *********************************************/ 1163 | 1164 | .no-transforms { 1165 | overflow-y: auto; 1166 | } 1167 | 1168 | .no-transforms .reveal .slides { 1169 | position: relative; 1170 | width: 80%; 1171 | height: auto !important; 1172 | top: 0; 1173 | left: 50%; 1174 | margin: 0; 1175 | text-align: center; 1176 | } 1177 | 1178 | .no-transforms .reveal .controls, 1179 | .no-transforms .reveal .progress { 1180 | display: none !important; 1181 | } 1182 | 1183 | .no-transforms .reveal .slides section { 1184 | display: block !important; 1185 | opacity: 1 !important; 1186 | position: relative !important; 1187 | height: auto; 1188 | min-height: auto; 1189 | top: 0; 1190 | left: -50%; 1191 | margin: 70px 0; 1192 | 1193 | -webkit-transform: none; 1194 | -moz-transform: none; 1195 | -ms-transform: none; 1196 | -o-transform: none; 1197 | transform: none; 1198 | } 1199 | 1200 | .no-transforms .reveal .slides section section { 1201 | left: 0; 1202 | } 1203 | 1204 | .reveal .no-transition, 1205 | .reveal .no-transition * { 1206 | -webkit-transition: none !important; 1207 | -moz-transition: none !important; 1208 | -ms-transition: none !important; 1209 | -o-transition: none !important; 1210 | transition: none !important; 1211 | } 1212 | 1213 | 1214 | /********************************************* 1215 | * BACKGROUND STATES [DEPRECATED] 1216 | *********************************************/ 1217 | 1218 | .reveal .state-background { 1219 | position: absolute; 1220 | width: 100%; 1221 | height: 100%; 1222 | background: rgba( 0, 0, 0, 0 ); 1223 | 1224 | -webkit-transition: background 800ms ease; 1225 | -moz-transition: background 800ms ease; 1226 | -ms-transition: background 800ms ease; 1227 | -o-transition: background 800ms ease; 1228 | transition: background 800ms ease; 1229 | } 1230 | .alert .reveal .state-background { 1231 | background: rgba( 200, 50, 30, 0.6 ); 1232 | } 1233 | .soothe .reveal .state-background { 1234 | background: rgba( 50, 200, 90, 0.4 ); 1235 | } 1236 | .blackout .reveal .state-background { 1237 | background: rgba( 0, 0, 0, 0.6 ); 1238 | } 1239 | .whiteout .reveal .state-background { 1240 | background: rgba( 255, 255, 255, 0.6 ); 1241 | } 1242 | .cobalt .reveal .state-background { 1243 | background: rgba( 22, 152, 213, 0.6 ); 1244 | } 1245 | .mint .reveal .state-background { 1246 | background: rgba( 22, 213, 75, 0.6 ); 1247 | } 1248 | .submerge .reveal .state-background { 1249 | background: rgba( 12, 25, 77, 0.6); 1250 | } 1251 | .lila .reveal .state-background { 1252 | background: rgba( 180, 50, 140, 0.6 ); 1253 | } 1254 | .sunset .reveal .state-background { 1255 | background: rgba( 255, 122, 0, 0.6 ); 1256 | } 1257 | 1258 | 1259 | /********************************************* 1260 | * PER-SLIDE BACKGROUNDS 1261 | *********************************************/ 1262 | 1263 | .reveal>.backgrounds { 1264 | position: absolute; 1265 | width: 100%; 1266 | height: 100%; 1267 | } 1268 | .reveal .slide-background { 1269 | position: absolute; 1270 | width: 100%; 1271 | height: 100%; 1272 | opacity: 0; 1273 | visibility: hidden; 1274 | 1275 | background-color: rgba( 0, 0, 0, 0 ); 1276 | background-position: 50% 50%; 1277 | background-repeat: no-repeat; 1278 | background-size: cover; 1279 | 1280 | -webkit-transition: all 600ms cubic-bezier(0.260, 0.860, 0.440, 0.985); 1281 | -moz-transition: all 600ms cubic-bezier(0.260, 0.860, 0.440, 0.985); 1282 | -ms-transition: all 600ms cubic-bezier(0.260, 0.860, 0.440, 0.985); 1283 | -o-transition: all 600ms cubic-bezier(0.260, 0.860, 0.440, 0.985); 1284 | transition: all 600ms cubic-bezier(0.260, 0.860, 0.440, 0.985); 1285 | } 1286 | .reveal .slide-background.present { 1287 | opacity: 1; 1288 | visibility: visible; 1289 | } 1290 | 1291 | .print-pdf .reveal .slide-background { 1292 | opacity: 1 !important; 1293 | visibility: visible !important; 1294 | } 1295 | 1296 | /* Immediate transition style */ 1297 | .reveal[data-background-transition=none]>.backgrounds .slide-background, 1298 | .reveal>.backgrounds .slide-background[data-background-transition=none] { 1299 | -webkit-transition: none; 1300 | -moz-transition: none; 1301 | -ms-transition: none; 1302 | -o-transition: none; 1303 | transition: none; 1304 | } 1305 | 1306 | /* Linear sliding transition style */ 1307 | .reveal[data-background-transition=slide]>.backgrounds .slide-background, 1308 | .reveal>.backgrounds .slide-background[data-background-transition=slide] { 1309 | opacity: 1; 1310 | 1311 | -webkit-backface-visibility: hidden; 1312 | -moz-backface-visibility: hidden; 1313 | -ms-backface-visibility: hidden; 1314 | backface-visibility: hidden; 1315 | 1316 | -webkit-transition-duration: 800ms; 1317 | -moz-transition-duration: 800ms; 1318 | -ms-transition-duration: 800ms; 1319 | -o-transition-duration: 800ms; 1320 | transition-duration: 800ms; 1321 | } 1322 | .reveal[data-background-transition=slide]>.backgrounds .slide-background.past, 1323 | .reveal>.backgrounds .slide-background.past[data-background-transition=slide] { 1324 | -webkit-transform: translate(-100%, 0); 1325 | -moz-transform: translate(-100%, 0); 1326 | -ms-transform: translate(-100%, 0); 1327 | -o-transform: translate(-100%, 0); 1328 | transform: translate(-100%, 0); 1329 | } 1330 | .reveal[data-background-transition=slide]>.backgrounds .slide-background.future, 1331 | .reveal>.backgrounds .slide-background.future[data-background-transition=slide] { 1332 | -webkit-transform: translate(100%, 0); 1333 | -moz-transform: translate(100%, 0); 1334 | -ms-transform: translate(100%, 0); 1335 | -o-transform: translate(100%, 0); 1336 | transform: translate(100%, 0); 1337 | } 1338 | 1339 | .reveal[data-background-transition=slide]>.backgrounds .slide-background>.slide-background.past, 1340 | .reveal>.backgrounds .slide-background>.slide-background.past[data-background-transition=slide] { 1341 | -webkit-transform: translate(0, -100%); 1342 | -moz-transform: translate(0, -100%); 1343 | -ms-transform: translate(0, -100%); 1344 | -o-transform: translate(0, -100%); 1345 | transform: translate(0, -100%); 1346 | } 1347 | .reveal[data-background-transition=slide]>.backgrounds .slide-background>.slide-background.future, 1348 | .reveal>.backgrounds .slide-background>.slide-background.future[data-background-transition=slide] { 1349 | -webkit-transform: translate(0, 100%); 1350 | -moz-transform: translate(0, 100%); 1351 | -ms-transform: translate(0, 100%); 1352 | -o-transform: translate(0, 100%); 1353 | transform: translate(0, 100%); 1354 | } 1355 | 1356 | 1357 | /* Global transition speed settings */ 1358 | .reveal[data-transition-speed="fast"]>.backgrounds .slide-background { 1359 | -webkit-transition-duration: 400ms; 1360 | -moz-transition-duration: 400ms; 1361 | -ms-transition-duration: 400ms; 1362 | transition-duration: 400ms; 1363 | } 1364 | .reveal[data-transition-speed="slow"]>.backgrounds .slide-background { 1365 | -webkit-transition-duration: 1200ms; 1366 | -moz-transition-duration: 1200ms; 1367 | -ms-transition-duration: 1200ms; 1368 | transition-duration: 1200ms; 1369 | } 1370 | 1371 | 1372 | /********************************************* 1373 | * RTL SUPPORT 1374 | *********************************************/ 1375 | 1376 | .reveal.rtl .slides, 1377 | .reveal.rtl .slides h1, 1378 | .reveal.rtl .slides h2, 1379 | .reveal.rtl .slides h3, 1380 | .reveal.rtl .slides h4, 1381 | .reveal.rtl .slides h5, 1382 | .reveal.rtl .slides h6 { 1383 | direction: rtl; 1384 | font-family: sans-serif; 1385 | } 1386 | 1387 | .reveal.rtl pre, 1388 | .reveal.rtl code { 1389 | direction: ltr; 1390 | } 1391 | 1392 | .reveal.rtl ol, 1393 | .reveal.rtl ul { 1394 | text-align: right; 1395 | } 1396 | 1397 | .reveal.rtl .progress span { 1398 | float: right 1399 | } 1400 | 1401 | 1402 | /********************************************* 1403 | * LINK PREVIEW OVERLAY 1404 | *********************************************/ 1405 | 1406 | .reveal .preview-link-overlay { 1407 | position: absolute; 1408 | top: 0; 1409 | left: 0; 1410 | width: 100%; 1411 | height: 100%; 1412 | z-index: 1000; 1413 | background: rgba( 0, 0, 0, 0.9 ); 1414 | opacity: 0; 1415 | visibility: hidden; 1416 | 1417 | -webkit-transition: all 0.3s ease; 1418 | -moz-transition: all 0.3s ease; 1419 | -ms-transition: all 0.3s ease; 1420 | transition: all 0.3s ease; 1421 | } 1422 | .reveal .preview-link-overlay.visible { 1423 | opacity: 1; 1424 | visibility: visible; 1425 | } 1426 | 1427 | .reveal .preview-link-overlay .spinner { 1428 | position: absolute; 1429 | display: block; 1430 | top: 50%; 1431 | left: 50%; 1432 | width: 32px; 1433 | height: 32px; 1434 | margin: -16px 0 0 -16px; 1435 | z-index: 10; 1436 | background-image: url(data:image/gif;base64,R0lGODlhIAAgAPMAAJmZmf%2F%2F%2F6%2Bvr8nJybW1tcDAwOjo6Nvb26ioqKOjo7Ozs%2FLy8vz8%2FAAAAAAAAAAAACH%2FC05FVFNDQVBFMi4wAwEAAAAh%2FhpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh%2BQQJCgAAACwAAAAAIAAgAAAE5xDISWlhperN52JLhSSdRgwVo1ICQZRUsiwHpTJT4iowNS8vyW2icCF6k8HMMBkCEDskxTBDAZwuAkkqIfxIQyhBQBFvAQSDITM5VDW6XNE4KagNh6Bgwe60smQUB3d4Rz1ZBApnFASDd0hihh12BkE9kjAJVlycXIg7CQIFA6SlnJ87paqbSKiKoqusnbMdmDC2tXQlkUhziYtyWTxIfy6BE8WJt5YJvpJivxNaGmLHT0VnOgSYf0dZXS7APdpB309RnHOG5gDqXGLDaC457D1zZ%2FV%2FnmOM82XiHRLYKhKP1oZmADdEAAAh%2BQQJCgAAACwAAAAAIAAgAAAE6hDISWlZpOrNp1lGNRSdRpDUolIGw5RUYhhHukqFu8DsrEyqnWThGvAmhVlteBvojpTDDBUEIFwMFBRAmBkSgOrBFZogCASwBDEY%2FCZSg7GSE0gSCjQBMVG023xWBhklAnoEdhQEfyNqMIcKjhRsjEdnezB%2BA4k8gTwJhFuiW4dokXiloUepBAp5qaKpp6%2BHo7aWW54wl7obvEe0kRuoplCGepwSx2jJvqHEmGt6whJpGpfJCHmOoNHKaHx61WiSR92E4lbFoq%2BB6QDtuetcaBPnW6%2BO7wDHpIiK9SaVK5GgV543tzjgGcghAgAh%2BQQJCgAAACwAAAAAIAAgAAAE7hDISSkxpOrN5zFHNWRdhSiVoVLHspRUMoyUakyEe8PTPCATW9A14E0UvuAKMNAZKYUZCiBMuBakSQKG8G2FzUWox2AUtAQFcBKlVQoLgQReZhQlCIJesQXI5B0CBnUMOxMCenoCfTCEWBsJColTMANldx15BGs8B5wlCZ9Po6OJkwmRpnqkqnuSrayqfKmqpLajoiW5HJq7FL1Gr2mMMcKUMIiJgIemy7xZtJsTmsM4xHiKv5KMCXqfyUCJEonXPN2rAOIAmsfB3uPoAK%2B%2BG%2Bw48edZPK%2BM6hLJpQg484enXIdQFSS1u6UhksENEQAAIfkECQoAAAAsAAAAACAAIAAABOcQyEmpGKLqzWcZRVUQnZYg1aBSh2GUVEIQ2aQOE%2BG%2BcD4ntpWkZQj1JIiZIogDFFyHI0UxQwFugMSOFIPJftfVAEoZLBbcLEFhlQiqGp1Vd140AUklUN3eCA51C1EWMzMCezCBBmkxVIVHBWd3HHl9JQOIJSdSnJ0TDKChCwUJjoWMPaGqDKannasMo6WnM562R5YluZRwur0wpgqZE7NKUm%2BFNRPIhjBJxKZteWuIBMN4zRMIVIhffcgojwCF117i4nlLnY5ztRLsnOk%2BaV%2BoJY7V7m76PdkS4trKcdg0Zc0tTcKkRAAAIfkECQoAAAAsAAAAACAAIAAABO4QyEkpKqjqzScpRaVkXZWQEximw1BSCUEIlDohrft6cpKCk5xid5MNJTaAIkekKGQkWyKHkvhKsR7ARmitkAYDYRIbUQRQjWBwJRzChi9CRlBcY1UN4g0%2FVNB0AlcvcAYHRyZPdEQFYV8ccwR5HWxEJ02YmRMLnJ1xCYp0Y5idpQuhopmmC2KgojKasUQDk5BNAwwMOh2RtRq5uQuPZKGIJQIGwAwGf6I0JXMpC8C7kXWDBINFMxS4DKMAWVWAGYsAdNqW5uaRxkSKJOZKaU3tPOBZ4DuK2LATgJhkPJMgTwKCdFjyPHEnKxFCDhEAACH5BAkKAAAALAAAAAAgACAAAATzEMhJaVKp6s2nIkolIJ2WkBShpkVRWqqQrhLSEu9MZJKK9y1ZrqYK9WiClmvoUaF8gIQSNeF1Er4MNFn4SRSDARWroAIETg1iVwuHjYB1kYc1mwruwXKC9gmsJXliGxc%2BXiUCby9ydh1sOSdMkpMTBpaXBzsfhoc5l58Gm5yToAaZhaOUqjkDgCWNHAULCwOLaTmzswadEqggQwgHuQsHIoZCHQMMQgQGubVEcxOPFAcMDAYUA85eWARmfSRQCdcMe0zeP1AAygwLlJtPNAAL19DARdPzBOWSm1brJBi45soRAWQAAkrQIykShQ9wVhHCwCQCACH5BAkKAAAALAAAAAAgACAAAATrEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiRMDjI0Fd30%2FiI2UA5GSS5UDj2l6NoqgOgN4gksEBgYFf0FDqKgHnyZ9OX8HrgYHdHpcHQULXAS2qKpENRg7eAMLC7kTBaixUYFkKAzWAAnLC7FLVxLWDBLKCwaKTULgEwbLA4hJtOkSBNqITT3xEgfLpBtzE%2FjiuL04RGEBgwWhShRgQExHBAAh%2BQQJCgAAACwAAAAAIAAgAAAE7xDISWlSqerNpyJKhWRdlSAVoVLCWk6JKlAqAavhO9UkUHsqlE6CwO1cRdCQ8iEIfzFVTzLdRAmZX3I2SfZiCqGk5dTESJeaOAlClzsJsqwiJwiqnFrb2nS9kmIcgEsjQydLiIlHehhpejaIjzh9eomSjZR%2BipslWIRLAgMDOR2DOqKogTB9pCUJBagDBXR6XB0EBkIIsaRsGGMMAxoDBgYHTKJiUYEGDAzHC9EACcUGkIgFzgwZ0QsSBcXHiQvOwgDdEwfFs0sDzt4S6BK4xYjkDOzn0unFeBzOBijIm1Dgmg5YFQwsCMjp1oJ8LyIAACH5BAkKAAAALAAAAAAgACAAAATwEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiUd6GGl6NoiPOH16iZKNlH6KmyWFOggHhEEvAwwMA0N9GBsEC6amhnVcEwavDAazGwIDaH1ipaYLBUTCGgQDA8NdHz0FpqgTBwsLqAbWAAnIA4FWKdMLGdYGEgraigbT0OITBcg5QwPT4xLrROZL6AuQAPUS7bxLpoWidY0JtxLHKhwwMJBTHgPKdEQAACH5BAkKAAAALAAAAAAgACAAAATrEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiUd6GAULDJCRiXo1CpGXDJOUjY%2BYip9DhToJA4RBLwMLCwVDfRgbBAaqqoZ1XBMHswsHtxtFaH1iqaoGNgAIxRpbFAgfPQSqpbgGBqUD1wBXeCYp1AYZ19JJOYgH1KwA4UBvQwXUBxPqVD9L3sbp2BNk2xvvFPJd%2BMFCN6HAAIKgNggY0KtEBAAh%2BQQJCgAAACwAAAAAIAAgAAAE6BDISWlSqerNpyJKhWRdlSAVoVLCWk6JKlAqAavhO9UkUHsqlE6CwO1cRdCQ8iEIfzFVTzLdRAmZX3I2SfYIDMaAFdTESJeaEDAIMxYFqrOUaNW4E4ObYcCXaiBVEgULe0NJaxxtYksjh2NLkZISgDgJhHthkpU4mW6blRiYmZOlh4JWkDqILwUGBnE6TYEbCgevr0N1gH4At7gHiRpFaLNrrq8HNgAJA70AWxQIH1%2BvsYMDAzZQPC9VCNkDWUhGkuE5PxJNwiUK4UfLzOlD4WvzAHaoG9nxPi5d%2BjYUqfAhhykOFwJWiAAAIfkECQoAAAAsAAAAACAAIAAABPAQyElpUqnqzaciSoVkXVUMFaFSwlpOCcMYlErAavhOMnNLNo8KsZsMZItJEIDIFSkLGQoQTNhIsFehRww2CQLKF0tYGKYSg%2BygsZIuNqJksKgbfgIGepNo2cIUB3V1B3IvNiBYNQaDSTtfhhx0CwVPI0UJe0%2Bbm4g5VgcGoqOcnjmjqDSdnhgEoamcsZuXO1aWQy8KAwOAuTYYGwi7w5h%2BKr0SJ8MFihpNbx%2B4Erq7BYBuzsdiH1jCAzoSfl0rVirNbRXlBBlLX%2BBP0XJLAPGzTkAuAOqb0WT5AH7OcdCm5B8TgRwSRKIHQtaLCwg1RAAAOwAAAAAAAAAAAA%3D%3D); 1437 | 1438 | visibility: visible; 1439 | opacity: 0.6; 1440 | 1441 | -webkit-transition: all 0.3s ease; 1442 | -moz-transition: all 0.3s ease; 1443 | -ms-transition: all 0.3s ease; 1444 | transition: all 0.3s ease; 1445 | } 1446 | 1447 | .reveal .preview-link-overlay header { 1448 | position: absolute; 1449 | left: 0; 1450 | top: 0; 1451 | width: 100%; 1452 | height: 40px; 1453 | z-index: 2; 1454 | border-bottom: 1px solid #222; 1455 | } 1456 | .reveal .preview-link-overlay header a { 1457 | display: inline-block; 1458 | width: 40px; 1459 | height: 40px; 1460 | padding: 0 10px; 1461 | float: right; 1462 | opacity: 0.6; 1463 | 1464 | box-sizing: border-box; 1465 | } 1466 | .reveal .preview-link-overlay header a:hover { 1467 | opacity: 1; 1468 | } 1469 | .reveal .preview-link-overlay header a .icon { 1470 | display: inline-block; 1471 | width: 20px; 1472 | height: 20px; 1473 | 1474 | background-position: 50% 50%; 1475 | background-size: 100%; 1476 | background-repeat: no-repeat; 1477 | } 1478 | .reveal .preview-link-overlay header a.close .icon { 1479 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABkklEQVRYR8WX4VHDMAxG6wnoJrABZQPYBCaBTWAD2g1gE5gg6OOsXuxIlr40d81dfrSJ9V4c2VLK7spHuTJ/5wpM07QXuXc5X0opX2tEJcadjHuV80li/FgxTIEK/5QBCICBD6xEhSMGHgQPgBgLiYVAB1dpSqKDawxTohFw4JSEA3clzgIBPCURwE2JucBR7rhPJJv5OpJwDX+SfDjgx1wACQeJG1aChP9K/IMmdZ8DtESV1WyP3Bt4MwM6sj4NMxMYiqUWHQu4KYA/SYkIjOsm3BXYWMKFDwU2khjCQ4ELJUJ4SmClRArOCmSXGuKma0fYD5CbzHxFpCSGAhfAVSSUGDUk2BWZaff2g6GE15BsBQ9nwmpIGDiyHQddwNTMKkbZaf9fajXQca1EX44puJZUsnY0ObGmITE3GVLCbEhQUjGVt146j6oasWN+49Vph2w1pZ5EansNZqKBm1txbU57iRRcZ86RWMDdWtBJUHBHwoQPi1GV+JCbntmvok7iTX4/Up9mgyTc/FJYDTcndgH/AA5A/CHsyEkVAAAAAElFTkSuQmCC); 1480 | } 1481 | .reveal .preview-link-overlay header a.external .icon { 1482 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAcElEQVRYR+2WSQoAIQwEzf8f7XiOMkUQxUPlGkM3hVmiQfQR9GYnH1SsAQlI4DiBqkCMoNb9y2e90IAEJPAcgdznU9+engMaeJ7Azh5Y1U67gAho4DqBqmB1buAf0MB1AlVBek83ZPkmJMGc1wAR+AAqod/B97TRpQAAAABJRU5ErkJggg==); 1483 | } 1484 | 1485 | .reveal .preview-link-overlay .viewport { 1486 | position: absolute; 1487 | top: 40px; 1488 | right: 0; 1489 | bottom: 0; 1490 | left: 0; 1491 | } 1492 | 1493 | .reveal .preview-link-overlay .viewport iframe { 1494 | width: 100%; 1495 | height: 100%; 1496 | max-width: 100%; 1497 | max-height: 100%; 1498 | border: 0; 1499 | 1500 | opacity: 0; 1501 | visibility: hidden; 1502 | 1503 | -webkit-transition: all 0.3s ease; 1504 | -moz-transition: all 0.3s ease; 1505 | -ms-transition: all 0.3s ease; 1506 | transition: all 0.3s ease; 1507 | } 1508 | 1509 | .reveal .preview-link-overlay.loaded .viewport iframe { 1510 | opacity: 1; 1511 | visibility: visible; 1512 | } 1513 | 1514 | .reveal .preview-link-overlay.loaded .spinner { 1515 | opacity: 0; 1516 | visibility: hidden; 1517 | 1518 | -webkit-transform: scale(0.2); 1519 | -moz-transform: scale(0.2); 1520 | -ms-transform: scale(0.2); 1521 | transform: scale(0.2); 1522 | } 1523 | 1524 | 1525 | /********************************************* 1526 | * ROLLING LINKS 1527 | *********************************************/ 1528 | 1529 | .reveal .roll { 1530 | display: inline-block; 1531 | line-height: 1.2; 1532 | overflow: hidden; 1533 | 1534 | vertical-align: top; 1535 | 1536 | -webkit-perspective: 400px; 1537 | -moz-perspective: 400px; 1538 | -ms-perspective: 400px; 1539 | perspective: 400px; 1540 | 1541 | -webkit-perspective-origin: 50% 50%; 1542 | -moz-perspective-origin: 50% 50%; 1543 | -ms-perspective-origin: 50% 50%; 1544 | perspective-origin: 50% 50%; 1545 | } 1546 | .reveal .roll:hover { 1547 | background: none; 1548 | text-shadow: none; 1549 | } 1550 | .reveal .roll span { 1551 | display: block; 1552 | position: relative; 1553 | padding: 0 2px; 1554 | 1555 | pointer-events: none; 1556 | 1557 | -webkit-transition: all 400ms ease; 1558 | -moz-transition: all 400ms ease; 1559 | -ms-transition: all 400ms ease; 1560 | transition: all 400ms ease; 1561 | 1562 | -webkit-transform-origin: 50% 0%; 1563 | -moz-transform-origin: 50% 0%; 1564 | -ms-transform-origin: 50% 0%; 1565 | transform-origin: 50% 0%; 1566 | 1567 | -webkit-transform-style: preserve-3d; 1568 | -moz-transform-style: preserve-3d; 1569 | -ms-transform-style: preserve-3d; 1570 | transform-style: preserve-3d; 1571 | 1572 | -webkit-backface-visibility: hidden; 1573 | -moz-backface-visibility: hidden; 1574 | backface-visibility: hidden; 1575 | } 1576 | .reveal .roll:hover span { 1577 | background: rgba(0,0,0,0.5); 1578 | 1579 | -webkit-transform: translate3d( 0px, 0px, -45px ) rotateX( 90deg ); 1580 | -moz-transform: translate3d( 0px, 0px, -45px ) rotateX( 90deg ); 1581 | -ms-transform: translate3d( 0px, 0px, -45px ) rotateX( 90deg ); 1582 | transform: translate3d( 0px, 0px, -45px ) rotateX( 90deg ); 1583 | } 1584 | .reveal .roll span:after { 1585 | content: attr(data-title); 1586 | 1587 | display: block; 1588 | position: absolute; 1589 | left: 0; 1590 | top: 0; 1591 | padding: 0 2px; 1592 | 1593 | -webkit-backface-visibility: hidden; 1594 | -moz-backface-visibility: hidden; 1595 | backface-visibility: hidden; 1596 | 1597 | -webkit-transform-origin: 50% 0%; 1598 | -moz-transform-origin: 50% 0%; 1599 | -ms-transform-origin: 50% 0%; 1600 | transform-origin: 50% 0%; 1601 | 1602 | -webkit-transform: translate3d( 0px, 110%, 0px ) rotateX( -90deg ); 1603 | -moz-transform: translate3d( 0px, 110%, 0px ) rotateX( -90deg ); 1604 | -ms-transform: translate3d( 0px, 110%, 0px ) rotateX( -90deg ); 1605 | transform: translate3d( 0px, 110%, 0px ) rotateX( -90deg ); 1606 | } 1607 | 1608 | 1609 | /********************************************* 1610 | * SPEAKER NOTES 1611 | *********************************************/ 1612 | 1613 | .reveal aside.notes { 1614 | display: none; 1615 | } 1616 | 1617 | 1618 | /********************************************* 1619 | * ZOOM PLUGIN 1620 | *********************************************/ 1621 | 1622 | .zoomed .reveal *, 1623 | .zoomed .reveal *:before, 1624 | .zoomed .reveal *:after { 1625 | -webkit-transform: none !important; 1626 | -moz-transform: none !important; 1627 | -ms-transform: none !important; 1628 | transform: none !important; 1629 | 1630 | -webkit-backface-visibility: visible !important; 1631 | -moz-backface-visibility: visible !important; 1632 | -ms-backface-visibility: visible !important; 1633 | backface-visibility: visible !important; 1634 | } 1635 | 1636 | .zoomed .reveal .progress, 1637 | .zoomed .reveal .controls { 1638 | opacity: 0; 1639 | } 1640 | 1641 | .zoomed .reveal .roll span { 1642 | background: none; 1643 | } 1644 | 1645 | .zoomed .reveal .roll span:after { 1646 | visibility: hidden; 1647 | } 1648 | 1649 | 1650 | -------------------------------------------------------------------------------- /presentacion_curso_2014/reveal.js/js/reveal.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * reveal.js 3 | * http://lab.hakim.se/reveal-js 4 | * MIT licensed 5 | * 6 | * Copyright (C) 2013 Hakim El Hattab, http://hakim.se 7 | */ 8 | var Reveal = (function(){ 9 | 10 | 'use strict'; 11 | 12 | var SLIDES_SELECTOR = '.reveal .slides section', 13 | HORIZONTAL_SLIDES_SELECTOR = '.reveal .slides>section', 14 | VERTICAL_SLIDES_SELECTOR = '.reveal .slides>section.present>section', 15 | HOME_SLIDE_SELECTOR = '.reveal .slides>section:first-child', 16 | 17 | // Configurations defaults, can be overridden at initialization time 18 | config = { 19 | 20 | // The "normal" size of the presentation, aspect ratio will be preserved 21 | // when the presentation is scaled to fit different resolutions 22 | width: 960, 23 | height: 700, 24 | 25 | // Factor of the display size that should remain empty around the content 26 | margin: 0.1, 27 | 28 | // Bounds for smallest/largest possible scale to apply to content 29 | minScale: 0.2, 30 | maxScale: 1.0, 31 | 32 | // Display controls in the bottom right corner 33 | controls: true, 34 | 35 | // Display a presentation progress bar 36 | progress: true, 37 | 38 | // Push each slide change to the browser history 39 | history: false, 40 | 41 | // Enable keyboard shortcuts for navigation 42 | keyboard: true, 43 | 44 | // Enable the slide overview mode 45 | overview: true, 46 | 47 | // Vertical centring of slides 48 | center: true, 49 | 50 | // Enables touch navigation on devices with touch input 51 | touch: true, 52 | 53 | // Loop the presentation 54 | loop: false, 55 | 56 | // Change the presentation direction to be RTL 57 | rtl: false, 58 | 59 | // Turns fragments on and off globally 60 | fragments: true, 61 | 62 | // Flags if the presentation is running in an embedded mode, 63 | // i.e. contained within a limited portion of the screen 64 | embedded: false, 65 | 66 | // Number of milliseconds between automatically proceeding to the 67 | // next slide, disabled when set to 0, this value can be overwritten 68 | // by using a data-autoslide attribute on your slides 69 | autoSlide: 0, 70 | 71 | // Enable slide navigation via mouse wheel 72 | mouseWheel: false, 73 | 74 | // Apply a 3D roll to links on hover 75 | rollingLinks: false, 76 | 77 | // Opens links in an iframe preview overlay 78 | previewLinks: false, 79 | 80 | // Theme (see /css/theme) 81 | theme: null, 82 | 83 | // Transition style 84 | transition: 'default', // default/cube/page/concave/zoom/linear/fade/none 85 | 86 | // Transition speed 87 | transitionSpeed: 'default', // default/fast/slow 88 | 89 | // Transition style for full page slide backgrounds 90 | backgroundTransition: 'default', // default/linear/none 91 | 92 | // Number of slides away from the current that are visible 93 | viewDistance: 3, 94 | 95 | // Script dependencies to load 96 | dependencies: [] 97 | }, 98 | 99 | // Flags if reveal.js is loaded (has dispatched the 'ready' event) 100 | loaded = false, 101 | 102 | // The current auto-slide duration 103 | autoSlide = 0, 104 | 105 | // The horizontal and vertical index of the currently active slide 106 | indexh, 107 | indexv, 108 | 109 | // The previous and current slide HTML elements 110 | previousSlide, 111 | currentSlide, 112 | 113 | // Slides may hold a data-state attribute which we pick up and apply 114 | // as a class to the body. This list contains the combined state of 115 | // all current slides. 116 | state = [], 117 | 118 | // The current scale of the presentation (see width/height config) 119 | scale = 1, 120 | 121 | // Cached references to DOM elements 122 | dom = {}, 123 | 124 | // Client support for CSS 3D transforms, see #checkCapabilities() 125 | supports3DTransforms, 126 | 127 | // Client support for CSS 2D transforms, see #checkCapabilities() 128 | supports2DTransforms, 129 | 130 | // Client is a mobile device, see #checkCapabilities() 131 | isMobileDevice, 132 | 133 | // Throttles mouse wheel navigation 134 | lastMouseWheelStep = 0, 135 | 136 | // An interval used to automatically move on to the next slide 137 | autoSlideTimeout = 0, 138 | 139 | // Delays updates to the URL due to a Chrome thumbnailer bug 140 | writeURLTimeout = 0, 141 | 142 | // A delay used to activate the overview mode 143 | activateOverviewTimeout = 0, 144 | 145 | // A delay used to deactivate the overview mode 146 | deactivateOverviewTimeout = 0, 147 | 148 | // Flags if the interaction event listeners are bound 149 | eventsAreBound = false, 150 | 151 | // Holds information about the currently ongoing touch input 152 | touch = { 153 | startX: 0, 154 | startY: 0, 155 | startSpan: 0, 156 | startCount: 0, 157 | captured: false, 158 | threshold: 40 159 | }; 160 | 161 | /** 162 | * Starts up the presentation if the client is capable. 163 | */ 164 | function initialize( options ) { 165 | 166 | checkCapabilities(); 167 | 168 | if( !supports2DTransforms && !supports3DTransforms ) { 169 | document.body.setAttribute( 'class', 'no-transforms' ); 170 | 171 | // If the browser doesn't support core features we won't be 172 | // using JavaScript to control the presentation 173 | return; 174 | } 175 | 176 | // Force a layout when the whole page, incl fonts, has loaded 177 | window.addEventListener( 'load', layout, false ); 178 | 179 | // Copy options over to our config object 180 | extend( config, options ); 181 | 182 | // Hide the address bar in mobile browsers 183 | hideAddressBar(); 184 | 185 | // Loads the dependencies and continues to #start() once done 186 | load(); 187 | 188 | } 189 | 190 | /** 191 | * Inspect the client to see what it's capable of, this 192 | * should only happens once per runtime. 193 | */ 194 | function checkCapabilities() { 195 | 196 | supports3DTransforms = 'WebkitPerspective' in document.body.style || 197 | 'MozPerspective' in document.body.style || 198 | 'msPerspective' in document.body.style || 199 | 'OPerspective' in document.body.style || 200 | 'perspective' in document.body.style; 201 | 202 | supports2DTransforms = 'WebkitTransform' in document.body.style || 203 | 'MozTransform' in document.body.style || 204 | 'msTransform' in document.body.style || 205 | 'OTransform' in document.body.style || 206 | 'transform' in document.body.style; 207 | 208 | isMobileDevice = navigator.userAgent.match( /(iphone|ipod|android)/gi ); 209 | 210 | } 211 | 212 | /** 213 | * Loads the dependencies of reveal.js. Dependencies are 214 | * defined via the configuration option 'dependencies' 215 | * and will be loaded prior to starting/binding reveal.js. 216 | * Some dependencies may have an 'async' flag, if so they 217 | * will load after reveal.js has been started up. 218 | */ 219 | function load() { 220 | 221 | var scripts = [], 222 | scriptsAsync = []; 223 | 224 | for( var i = 0, len = config.dependencies.length; i < len; i++ ) { 225 | var s = config.dependencies[i]; 226 | 227 | // Load if there's no condition or the condition is truthy 228 | if( !s.condition || s.condition() ) { 229 | if( s.async ) { 230 | scriptsAsync.push( s.src ); 231 | } 232 | else { 233 | scripts.push( s.src ); 234 | } 235 | 236 | // Extension may contain callback functions 237 | if( typeof s.callback === 'function' ) { 238 | head.ready( s.src.match( /([\w\d_\-]*)\.?js$|[^\\\/]*$/i )[0], s.callback ); 239 | } 240 | } 241 | } 242 | 243 | // Called once synchronous scripts finish loading 244 | function proceed() { 245 | if( scriptsAsync.length ) { 246 | // Load asynchronous scripts 247 | head.js.apply( null, scriptsAsync ); 248 | } 249 | 250 | start(); 251 | } 252 | 253 | if( scripts.length ) { 254 | head.ready( proceed ); 255 | 256 | // Load synchronous scripts 257 | head.js.apply( null, scripts ); 258 | } 259 | else { 260 | proceed(); 261 | } 262 | 263 | } 264 | 265 | /** 266 | * Starts up reveal.js by binding input events and navigating 267 | * to the current URL deeplink if there is one. 268 | */ 269 | function start() { 270 | 271 | // Make sure we've got all the DOM elements we need 272 | setupDOM(); 273 | 274 | // Decorate the slide DOM elements with state classes (past/future) 275 | setupSlides(); 276 | 277 | // Updates the presentation to match the current configuration values 278 | configure(); 279 | 280 | // Read the initial hash 281 | readURL(); 282 | 283 | // Notify listeners that the presentation is ready but use a 1ms 284 | // timeout to ensure it's not fired synchronously after #initialize() 285 | setTimeout( function() { 286 | // Enable transitions now that we're loaded 287 | dom.slides.classList.remove( 'no-transition' ); 288 | 289 | loaded = true; 290 | 291 | dispatchEvent( 'ready', { 292 | 'indexh': indexh, 293 | 'indexv': indexv, 294 | 'currentSlide': currentSlide 295 | } ); 296 | }, 1 ); 297 | 298 | } 299 | 300 | /** 301 | * Iterates through and decorates slides DOM elements with 302 | * appropriate classes. 303 | */ 304 | function setupSlides() { 305 | 306 | var horizontalSlides = toArray( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ); 307 | horizontalSlides.forEach( function( horizontalSlide ) { 308 | 309 | var verticalSlides = toArray( horizontalSlide.querySelectorAll( 'section' ) ); 310 | verticalSlides.forEach( function( verticalSlide, y ) { 311 | 312 | if( y > 0 ) verticalSlide.classList.add( 'future' ); 313 | 314 | } ); 315 | 316 | } ); 317 | 318 | } 319 | 320 | /** 321 | * Finds and stores references to DOM elements which are 322 | * required by the presentation. If a required element is 323 | * not found, it is created. 324 | */ 325 | function setupDOM() { 326 | 327 | // Cache references to key DOM elements 328 | dom.theme = document.querySelector( '#theme' ); 329 | dom.wrapper = document.querySelector( '.reveal' ); 330 | dom.slides = document.querySelector( '.reveal .slides' ); 331 | 332 | // Prevent transitions while we're loading 333 | dom.slides.classList.add( 'no-transition' ); 334 | 335 | // Background element 336 | dom.background = createSingletonNode( dom.wrapper, 'div', 'backgrounds', null ); 337 | 338 | // Progress bar 339 | dom.progress = createSingletonNode( dom.wrapper, 'div', 'progress', '' ); 340 | dom.progressbar = dom.progress.querySelector( 'span' ); 341 | 342 | // Arrow controls 343 | createSingletonNode( dom.wrapper, 'aside', 'controls', 344 | '' + 345 | '' + 346 | '' + 347 | '' ); 348 | 349 | // State background element [DEPRECATED] 350 | createSingletonNode( dom.wrapper, 'div', 'state-background', null ); 351 | 352 | // Overlay graphic which is displayed during the paused mode 353 | createSingletonNode( dom.wrapper, 'div', 'pause-overlay', null ); 354 | 355 | // Cache references to elements 356 | if ( config.controls ) { 357 | dom.controls = document.querySelector( '.reveal .controls' ); 358 | 359 | // There can be multiple instances of controls throughout the page 360 | dom.controlsLeft = toArray( document.querySelectorAll( '.navigate-left' ) ); 361 | dom.controlsRight = toArray( document.querySelectorAll( '.navigate-right' ) ); 362 | dom.controlsUp = toArray( document.querySelectorAll( '.navigate-up' ) ); 363 | dom.controlsDown = toArray( document.querySelectorAll( '.navigate-down' ) ); 364 | dom.controlsPrev = toArray( document.querySelectorAll( '.navigate-prev' ) ); 365 | dom.controlsNext = toArray( document.querySelectorAll( '.navigate-next' ) ); 366 | } 367 | 368 | } 369 | 370 | /** 371 | * Creates an HTML element and returns a reference to it. 372 | * If the element already exists the existing instance will 373 | * be returned. 374 | */ 375 | function createSingletonNode( container, tagname, classname, innerHTML ) { 376 | 377 | var node = container.querySelector( '.' + classname ); 378 | if( !node ) { 379 | node = document.createElement( tagname ); 380 | node.classList.add( classname ); 381 | if( innerHTML !== null ) { 382 | node.innerHTML = innerHTML; 383 | } 384 | container.appendChild( node ); 385 | } 386 | return node; 387 | 388 | } 389 | 390 | /** 391 | * Creates the slide background elements and appends them 392 | * to the background container. One element is created per 393 | * slide no matter if the given slide has visible background. 394 | */ 395 | function createBackgrounds() { 396 | 397 | if( isPrintingPDF() ) { 398 | document.body.classList.add( 'print-pdf' ); 399 | } 400 | 401 | // Clear prior backgrounds 402 | dom.background.innerHTML = ''; 403 | dom.background.classList.add( 'no-transition' ); 404 | 405 | // Helper method for creating a background element for the 406 | // given slide 407 | function _createBackground( slide, container ) { 408 | 409 | var data = { 410 | background: slide.getAttribute( 'data-background' ), 411 | backgroundSize: slide.getAttribute( 'data-background-size' ), 412 | backgroundImage: slide.getAttribute( 'data-background-image' ), 413 | backgroundColor: slide.getAttribute( 'data-background-color' ), 414 | backgroundRepeat: slide.getAttribute( 'data-background-repeat' ), 415 | backgroundPosition: slide.getAttribute( 'data-background-position' ), 416 | backgroundTransition: slide.getAttribute( 'data-background-transition' ) 417 | }; 418 | 419 | var element = document.createElement( 'div' ); 420 | element.className = 'slide-background'; 421 | 422 | if( data.background ) { 423 | // Auto-wrap image urls in url(...) 424 | if( /^(http|file|\/\/)/gi.test( data.background ) || /\.(png|jpg|jpeg|gif|bmp)$/gi.test( data.background ) ) { 425 | element.style.backgroundImage = 'url('+ data.background +')'; 426 | } 427 | else { 428 | element.style.background = data.background; 429 | } 430 | } 431 | 432 | // Additional and optional background properties 433 | if( data.backgroundSize ) element.style.backgroundSize = data.backgroundSize; 434 | if( data.backgroundImage ) element.style.backgroundImage = 'url("' + data.backgroundImage + '")'; 435 | if( data.backgroundColor ) element.style.backgroundColor = data.backgroundColor; 436 | if( data.backgroundRepeat ) element.style.backgroundRepeat = data.backgroundRepeat; 437 | if( data.backgroundPosition ) element.style.backgroundPosition = data.backgroundPosition; 438 | if( data.backgroundTransition ) element.setAttribute( 'data-background-transition', data.backgroundTransition ); 439 | 440 | container.appendChild( element ); 441 | 442 | return element; 443 | 444 | } 445 | 446 | // Iterate over all horizontal slides 447 | toArray( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ).forEach( function( slideh ) { 448 | 449 | var backgroundStack; 450 | 451 | if( isPrintingPDF() ) { 452 | backgroundStack = _createBackground( slideh, slideh ); 453 | } 454 | else { 455 | backgroundStack = _createBackground( slideh, dom.background ); 456 | } 457 | 458 | // Iterate over all vertical slides 459 | toArray( slideh.querySelectorAll( 'section' ) ).forEach( function( slidev ) { 460 | 461 | if( isPrintingPDF() ) { 462 | _createBackground( slidev, slidev ); 463 | } 464 | else { 465 | _createBackground( slidev, backgroundStack ); 466 | } 467 | 468 | } ); 469 | 470 | } ); 471 | 472 | } 473 | 474 | /** 475 | * Applies the configuration settings from the config 476 | * object. May be called multiple times. 477 | */ 478 | function configure( options ) { 479 | 480 | dom.wrapper.classList.remove( config.transition ); 481 | 482 | // New config options may be passed when this method 483 | // is invoked through the API after initialization 484 | if( typeof options === 'object' ) extend( config, options ); 485 | 486 | // Force linear transition based on browser capabilities 487 | if( supports3DTransforms === false ) config.transition = 'linear'; 488 | 489 | dom.wrapper.classList.add( config.transition ); 490 | 491 | dom.wrapper.setAttribute( 'data-transition-speed', config.transitionSpeed ); 492 | dom.wrapper.setAttribute( 'data-background-transition', config.backgroundTransition ); 493 | 494 | if( dom.controls ) { 495 | dom.controls.style.display = ( config.controls && dom.controls ) ? 'block' : 'none'; 496 | } 497 | 498 | if( dom.progress ) { 499 | dom.progress.style.display = ( config.progress && dom.progress ) ? 'block' : 'none'; 500 | } 501 | 502 | if( config.rtl ) { 503 | dom.wrapper.classList.add( 'rtl' ); 504 | } 505 | else { 506 | dom.wrapper.classList.remove( 'rtl' ); 507 | } 508 | 509 | if( config.center ) { 510 | dom.wrapper.classList.add( 'center' ); 511 | } 512 | else { 513 | dom.wrapper.classList.remove( 'center' ); 514 | } 515 | 516 | if( config.mouseWheel ) { 517 | document.addEventListener( 'DOMMouseScroll', onDocumentMouseScroll, false ); // FF 518 | document.addEventListener( 'mousewheel', onDocumentMouseScroll, false ); 519 | } 520 | else { 521 | document.removeEventListener( 'DOMMouseScroll', onDocumentMouseScroll, false ); // FF 522 | document.removeEventListener( 'mousewheel', onDocumentMouseScroll, false ); 523 | } 524 | 525 | // Rolling 3D links 526 | if( config.rollingLinks ) { 527 | enableRollingLinks(); 528 | } 529 | else { 530 | disableRollingLinks(); 531 | } 532 | 533 | // Iframe link previews 534 | if( config.previewLinks ) { 535 | enablePreviewLinks(); 536 | } 537 | else { 538 | disablePreviewLinks(); 539 | enablePreviewLinks( '[data-preview-link]' ); 540 | } 541 | 542 | // Load the theme in the config, if it's not already loaded 543 | if( config.theme && dom.theme ) { 544 | var themeURL = dom.theme.getAttribute( 'href' ); 545 | var themeFinder = /[^\/]*?(?=\.css)/; 546 | var themeName = themeURL.match(themeFinder)[0]; 547 | 548 | if( config.theme !== themeName ) { 549 | themeURL = themeURL.replace(themeFinder, config.theme); 550 | dom.theme.setAttribute( 'href', themeURL ); 551 | } 552 | } 553 | 554 | sync(); 555 | 556 | } 557 | 558 | /** 559 | * Binds all event listeners. 560 | */ 561 | function addEventListeners() { 562 | 563 | eventsAreBound = true; 564 | 565 | window.addEventListener( 'hashchange', onWindowHashChange, false ); 566 | window.addEventListener( 'resize', onWindowResize, false ); 567 | 568 | if( config.touch ) { 569 | dom.wrapper.addEventListener( 'touchstart', onTouchStart, false ); 570 | dom.wrapper.addEventListener( 'touchmove', onTouchMove, false ); 571 | dom.wrapper.addEventListener( 'touchend', onTouchEnd, false ); 572 | 573 | // Support pointer-style touch interaction as well 574 | if( window.navigator.msPointerEnabled ) { 575 | dom.wrapper.addEventListener( 'MSPointerDown', onPointerDown, false ); 576 | dom.wrapper.addEventListener( 'MSPointerMove', onPointerMove, false ); 577 | dom.wrapper.addEventListener( 'MSPointerUp', onPointerUp, false ); 578 | } 579 | } 580 | 581 | if( config.keyboard ) { 582 | document.addEventListener( 'keydown', onDocumentKeyDown, false ); 583 | } 584 | 585 | if ( config.progress && dom.progress ) { 586 | dom.progress.addEventListener( 'click', onProgressClicked, false ); 587 | } 588 | 589 | if ( config.controls && dom.controls ) { 590 | [ 'touchstart', 'click' ].forEach( function( eventName ) { 591 | dom.controlsLeft.forEach( function( el ) { el.addEventListener( eventName, onNavigateLeftClicked, false ); } ); 592 | dom.controlsRight.forEach( function( el ) { el.addEventListener( eventName, onNavigateRightClicked, false ); } ); 593 | dom.controlsUp.forEach( function( el ) { el.addEventListener( eventName, onNavigateUpClicked, false ); } ); 594 | dom.controlsDown.forEach( function( el ) { el.addEventListener( eventName, onNavigateDownClicked, false ); } ); 595 | dom.controlsPrev.forEach( function( el ) { el.addEventListener( eventName, onNavigatePrevClicked, false ); } ); 596 | dom.controlsNext.forEach( function( el ) { el.addEventListener( eventName, onNavigateNextClicked, false ); } ); 597 | } ); 598 | } 599 | 600 | } 601 | 602 | /** 603 | * Unbinds all event listeners. 604 | */ 605 | function removeEventListeners() { 606 | 607 | eventsAreBound = false; 608 | 609 | document.removeEventListener( 'keydown', onDocumentKeyDown, false ); 610 | window.removeEventListener( 'hashchange', onWindowHashChange, false ); 611 | window.removeEventListener( 'resize', onWindowResize, false ); 612 | 613 | dom.wrapper.removeEventListener( 'touchstart', onTouchStart, false ); 614 | dom.wrapper.removeEventListener( 'touchmove', onTouchMove, false ); 615 | dom.wrapper.removeEventListener( 'touchend', onTouchEnd, false ); 616 | 617 | if( window.navigator.msPointerEnabled ) { 618 | dom.wrapper.removeEventListener( 'MSPointerDown', onPointerDown, false ); 619 | dom.wrapper.removeEventListener( 'MSPointerMove', onPointerMove, false ); 620 | dom.wrapper.removeEventListener( 'MSPointerUp', onPointerUp, false ); 621 | } 622 | 623 | if ( config.progress && dom.progress ) { 624 | dom.progress.removeEventListener( 'click', onProgressClicked, false ); 625 | } 626 | 627 | if ( config.controls && dom.controls ) { 628 | [ 'touchstart', 'click' ].forEach( function( eventName ) { 629 | dom.controlsLeft.forEach( function( el ) { el.removeEventListener( eventName, onNavigateLeftClicked, false ); } ); 630 | dom.controlsRight.forEach( function( el ) { el.removeEventListener( eventName, onNavigateRightClicked, false ); } ); 631 | dom.controlsUp.forEach( function( el ) { el.removeEventListener( eventName, onNavigateUpClicked, false ); } ); 632 | dom.controlsDown.forEach( function( el ) { el.removeEventListener( eventName, onNavigateDownClicked, false ); } ); 633 | dom.controlsPrev.forEach( function( el ) { el.removeEventListener( eventName, onNavigatePrevClicked, false ); } ); 634 | dom.controlsNext.forEach( function( el ) { el.removeEventListener( eventName, onNavigateNextClicked, false ); } ); 635 | } ); 636 | } 637 | 638 | } 639 | 640 | /** 641 | * Extend object a with the properties of object b. 642 | * If there's a conflict, object b takes precedence. 643 | */ 644 | function extend( a, b ) { 645 | 646 | for( var i in b ) { 647 | a[ i ] = b[ i ]; 648 | } 649 | 650 | } 651 | 652 | /** 653 | * Converts the target object to an array. 654 | */ 655 | function toArray( o ) { 656 | 657 | return Array.prototype.slice.call( o ); 658 | 659 | } 660 | 661 | /** 662 | * Measures the distance in pixels between point a 663 | * and point b. 664 | * 665 | * @param {Object} a point with x/y properties 666 | * @param {Object} b point with x/y properties 667 | */ 668 | function distanceBetween( a, b ) { 669 | 670 | var dx = a.x - b.x, 671 | dy = a.y - b.y; 672 | 673 | return Math.sqrt( dx*dx + dy*dy ); 674 | 675 | } 676 | 677 | /** 678 | * Applies a CSS transform to the target element. 679 | */ 680 | function transformElement( element, transform ) { 681 | 682 | element.style.WebkitTransform = transform; 683 | element.style.MozTransform = transform; 684 | element.style.msTransform = transform; 685 | element.style.OTransform = transform; 686 | element.style.transform = transform; 687 | 688 | } 689 | 690 | /** 691 | * Retrieves the height of the given element by looking 692 | * at the position and height of its immediate children. 693 | */ 694 | function getAbsoluteHeight( element ) { 695 | 696 | var height = 0; 697 | 698 | if( element ) { 699 | var absoluteChildren = 0; 700 | 701 | toArray( element.childNodes ).forEach( function( child ) { 702 | 703 | if( typeof child.offsetTop === 'number' && child.style ) { 704 | // Count # of abs children 705 | if( child.style.position === 'absolute' ) { 706 | absoluteChildren += 1; 707 | } 708 | 709 | height = Math.max( height, child.offsetTop + child.offsetHeight ); 710 | } 711 | 712 | } ); 713 | 714 | // If there are no absolute children, use offsetHeight 715 | if( absoluteChildren === 0 ) { 716 | height = element.offsetHeight; 717 | } 718 | 719 | } 720 | 721 | return height; 722 | 723 | } 724 | 725 | /** 726 | * Returns the remaining height within the parent of the 727 | * target element after subtracting the height of all 728 | * siblings. 729 | * 730 | * remaining height = [parent height] - [ siblings height] 731 | */ 732 | function getRemainingHeight( element, height ) { 733 | 734 | height = height || 0; 735 | 736 | if( element ) { 737 | var parent = element.parentNode; 738 | var siblings = parent.childNodes; 739 | 740 | // Subtract the height of each sibling 741 | toArray( siblings ).forEach( function( sibling ) { 742 | 743 | if( typeof sibling.offsetHeight === 'number' && sibling !== element ) { 744 | 745 | var styles = window.getComputedStyle( sibling ), 746 | marginTop = parseInt( styles.marginTop, 10 ), 747 | marginBottom = parseInt( styles.marginBottom, 10 ); 748 | 749 | height -= sibling.offsetHeight + marginTop + marginBottom; 750 | 751 | } 752 | 753 | } ); 754 | 755 | var elementStyles = window.getComputedStyle( element ); 756 | 757 | // Subtract the margins of the target element 758 | height -= parseInt( elementStyles.marginTop, 10 ) + 759 | parseInt( elementStyles.marginBottom, 10 ); 760 | 761 | } 762 | 763 | return height; 764 | 765 | } 766 | 767 | /** 768 | * Checks if this instance is being used to print a PDF. 769 | */ 770 | function isPrintingPDF() { 771 | 772 | return ( /print-pdf/gi ).test( window.location.search ); 773 | 774 | } 775 | 776 | /** 777 | * Hides the address bar if we're on a mobile device. 778 | */ 779 | function hideAddressBar() { 780 | 781 | if( /iphone|ipod|android/gi.test( navigator.userAgent ) && !/crios/gi.test( navigator.userAgent ) ) { 782 | // Events that should trigger the address bar to hide 783 | window.addEventListener( 'load', removeAddressBar, false ); 784 | window.addEventListener( 'orientationchange', removeAddressBar, false ); 785 | } 786 | 787 | } 788 | 789 | /** 790 | * Causes the address bar to hide on mobile devices, 791 | * more vertical space ftw. 792 | */ 793 | function removeAddressBar() { 794 | 795 | if( window.orientation === 0 ) { 796 | document.documentElement.style.overflow = 'scroll'; 797 | document.body.style.height = '120%'; 798 | } 799 | else { 800 | document.documentElement.style.overflow = ''; 801 | document.body.style.height = '100%'; 802 | } 803 | 804 | setTimeout( function() { 805 | window.scrollTo( 0, 1 ); 806 | }, 10 ); 807 | 808 | } 809 | 810 | /** 811 | * Dispatches an event of the specified type from the 812 | * reveal DOM element. 813 | */ 814 | function dispatchEvent( type, properties ) { 815 | 816 | var event = document.createEvent( "HTMLEvents", 1, 2 ); 817 | event.initEvent( type, true, true ); 818 | extend( event, properties ); 819 | dom.wrapper.dispatchEvent( event ); 820 | 821 | } 822 | 823 | /** 824 | * Wrap all links in 3D goodness. 825 | */ 826 | function enableRollingLinks() { 827 | 828 | if( supports3DTransforms && !( 'msPerspective' in document.body.style ) ) { 829 | var anchors = document.querySelectorAll( SLIDES_SELECTOR + ' a:not(.image)' ); 830 | 831 | for( var i = 0, len = anchors.length; i < len; i++ ) { 832 | var anchor = anchors[i]; 833 | 834 | if( anchor.textContent && !anchor.querySelector( '*' ) && ( !anchor.className || !anchor.classList.contains( anchor, 'roll' ) ) ) { 835 | var span = document.createElement('span'); 836 | span.setAttribute('data-title', anchor.text); 837 | span.innerHTML = anchor.innerHTML; 838 | 839 | anchor.classList.add( 'roll' ); 840 | anchor.innerHTML = ''; 841 | anchor.appendChild(span); 842 | } 843 | } 844 | } 845 | 846 | } 847 | 848 | /** 849 | * Unwrap all 3D links. 850 | */ 851 | function disableRollingLinks() { 852 | 853 | var anchors = document.querySelectorAll( SLIDES_SELECTOR + ' a.roll' ); 854 | 855 | for( var i = 0, len = anchors.length; i < len; i++ ) { 856 | var anchor = anchors[i]; 857 | var span = anchor.querySelector( 'span' ); 858 | 859 | if( span ) { 860 | anchor.classList.remove( 'roll' ); 861 | anchor.innerHTML = span.innerHTML; 862 | } 863 | } 864 | 865 | } 866 | 867 | /** 868 | * Bind preview frame links. 869 | */ 870 | function enablePreviewLinks( selector ) { 871 | 872 | var anchors = toArray( document.querySelectorAll( selector ? selector : 'a' ) ); 873 | 874 | anchors.forEach( function( element ) { 875 | if( /^(http|www)/gi.test( element.getAttribute( 'href' ) ) ) { 876 | element.addEventListener( 'click', onPreviewLinkClicked, false ); 877 | } 878 | } ); 879 | 880 | } 881 | 882 | /** 883 | * Unbind preview frame links. 884 | */ 885 | function disablePreviewLinks() { 886 | 887 | var anchors = toArray( document.querySelectorAll( 'a' ) ); 888 | 889 | anchors.forEach( function( element ) { 890 | if( /^(http|www)/gi.test( element.getAttribute( 'href' ) ) ) { 891 | element.removeEventListener( 'click', onPreviewLinkClicked, false ); 892 | } 893 | } ); 894 | 895 | } 896 | 897 | /** 898 | * Opens a preview window for the target URL. 899 | */ 900 | function openPreview( url ) { 901 | 902 | closePreview(); 903 | 904 | dom.preview = document.createElement( 'div' ); 905 | dom.preview.classList.add( 'preview-link-overlay' ); 906 | dom.wrapper.appendChild( dom.preview ); 907 | 908 | dom.preview.innerHTML = [ 909 | '
', 910 | '', 911 | '', 912 | '
', 913 | '
', 914 | '
', 915 | '', 916 | '
' 917 | ].join(''); 918 | 919 | dom.preview.querySelector( 'iframe' ).addEventListener( 'load', function( event ) { 920 | dom.preview.classList.add( 'loaded' ); 921 | }, false ); 922 | 923 | dom.preview.querySelector( '.close' ).addEventListener( 'click', function( event ) { 924 | closePreview(); 925 | event.preventDefault(); 926 | }, false ); 927 | 928 | dom.preview.querySelector( '.external' ).addEventListener( 'click', function( event ) { 929 | closePreview(); 930 | }, false ); 931 | 932 | setTimeout( function() { 933 | dom.preview.classList.add( 'visible' ); 934 | }, 1 ); 935 | 936 | } 937 | 938 | /** 939 | * Closes the iframe preview window. 940 | */ 941 | function closePreview() { 942 | 943 | if( dom.preview ) { 944 | dom.preview.setAttribute( 'src', '' ); 945 | dom.preview.parentNode.removeChild( dom.preview ); 946 | dom.preview = null; 947 | } 948 | 949 | } 950 | 951 | /** 952 | * Return a sorted fragments list, ordered by an increasing 953 | * "data-fragment-index" attribute. 954 | * 955 | * Fragments will be revealed in the order that they are returned by 956 | * this function, so you can use the index attributes to control the 957 | * order of fragment appearance. 958 | * 959 | * To maintain a sensible default fragment order, fragments are presumed 960 | * to be passed in document order. This function adds a "fragment-index" 961 | * attribute to each node if such an attribute is not already present, 962 | * and sets that attribute to an integer value which is the position of 963 | * the fragment within the fragments list. 964 | */ 965 | function sortFragments( fragments ) { 966 | 967 | var a = toArray( fragments ); 968 | 969 | a.forEach( function( el, idx ) { 970 | if( !el.hasAttribute( 'data-fragment-index' ) ) { 971 | el.setAttribute( 'data-fragment-index', idx ); 972 | } 973 | } ); 974 | 975 | a.sort( function( l, r ) { 976 | return l.getAttribute( 'data-fragment-index' ) - r.getAttribute( 'data-fragment-index'); 977 | } ); 978 | 979 | return a; 980 | 981 | } 982 | 983 | /** 984 | * Applies JavaScript-controlled layout rules to the 985 | * presentation. 986 | */ 987 | function layout() { 988 | 989 | if( dom.wrapper && !isPrintingPDF() ) { 990 | 991 | // Available space to scale within 992 | var availableWidth = dom.wrapper.offsetWidth, 993 | availableHeight = dom.wrapper.offsetHeight; 994 | 995 | // Reduce available space by margin 996 | availableWidth -= ( availableHeight * config.margin ); 997 | availableHeight -= ( availableHeight * config.margin ); 998 | 999 | // Dimensions of the content 1000 | var slideWidth = config.width, 1001 | slideHeight = config.height, 1002 | slidePadding = 20; // TODO Dig this out of DOM 1003 | 1004 | // Layout the contents of the slides 1005 | layoutSlideContents( config.width, config.height, slidePadding ); 1006 | 1007 | // Slide width may be a percentage of available width 1008 | if( typeof slideWidth === 'string' && /%$/.test( slideWidth ) ) { 1009 | slideWidth = parseInt( slideWidth, 10 ) / 100 * availableWidth; 1010 | } 1011 | 1012 | // Slide height may be a percentage of available height 1013 | if( typeof slideHeight === 'string' && /%$/.test( slideHeight ) ) { 1014 | slideHeight = parseInt( slideHeight, 10 ) / 100 * availableHeight; 1015 | } 1016 | 1017 | dom.slides.style.width = slideWidth + 'px'; 1018 | dom.slides.style.height = slideHeight + 'px'; 1019 | 1020 | // Determine scale of content to fit within available space 1021 | scale = Math.min( availableWidth / slideWidth, availableHeight / slideHeight ); 1022 | 1023 | // Respect max/min scale settings 1024 | scale = Math.max( scale, config.minScale ); 1025 | scale = Math.min( scale, config.maxScale ); 1026 | 1027 | // Prefer applying scale via zoom since Chrome blurs scaled content 1028 | // with nested transforms 1029 | if( typeof dom.slides.style.zoom !== 'undefined' && !navigator.userAgent.match( /(iphone|ipod|ipad|android)/gi ) ) { 1030 | dom.slides.style.zoom = scale; 1031 | } 1032 | // Apply scale transform as a fallback 1033 | else { 1034 | transformElement( dom.slides, 'translate(-50%, -50%) scale('+ scale +') translate(50%, 50%)' ); 1035 | } 1036 | 1037 | // Select all slides, vertical and horizontal 1038 | var slides = toArray( document.querySelectorAll( SLIDES_SELECTOR ) ); 1039 | 1040 | for( var i = 0, len = slides.length; i < len; i++ ) { 1041 | var slide = slides[ i ]; 1042 | 1043 | // Don't bother updating invisible slides 1044 | if( slide.style.display === 'none' ) { 1045 | continue; 1046 | } 1047 | 1048 | if( config.center ) { 1049 | // Vertical stacks are not centred since their section 1050 | // children will be 1051 | if( slide.classList.contains( 'stack' ) ) { 1052 | slide.style.top = 0; 1053 | } 1054 | else { 1055 | slide.style.top = Math.max( - ( getAbsoluteHeight( slide ) / 2 ) - slidePadding, -slideHeight / 2 ) + 'px'; 1056 | } 1057 | } 1058 | else { 1059 | slide.style.top = ''; 1060 | } 1061 | 1062 | } 1063 | 1064 | updateProgress(); 1065 | 1066 | } 1067 | 1068 | } 1069 | 1070 | /** 1071 | * Applies layout logic to the contents of all slides in 1072 | * the presentation. 1073 | */ 1074 | function layoutSlideContents( width, height, padding ) { 1075 | 1076 | // Handle sizing of elements with the 'stretch' class 1077 | toArray( dom.slides.querySelectorAll( 'section > .stretch' ) ).forEach( function( element ) { 1078 | 1079 | // Determine how much vertical space we can use 1080 | var remainingHeight = getRemainingHeight( element, ( height - ( padding * 2 ) ) ); 1081 | 1082 | // Consider the aspect ratio of media elements 1083 | if( /(img|video)/gi.test( element.nodeName ) ) { 1084 | var nw = element.naturalWidth || element.videoWidth, 1085 | nh = element.naturalHeight || element.videoHeight; 1086 | 1087 | var es = Math.min( width / nw, remainingHeight / nh ); 1088 | 1089 | element.style.width = ( nw * es ) + 'px'; 1090 | element.style.height = ( nh * es ) + 'px'; 1091 | 1092 | } 1093 | else { 1094 | element.style.width = width + 'px'; 1095 | element.style.height = remainingHeight + 'px'; 1096 | } 1097 | 1098 | } ); 1099 | 1100 | } 1101 | 1102 | /** 1103 | * Stores the vertical index of a stack so that the same 1104 | * vertical slide can be selected when navigating to and 1105 | * from the stack. 1106 | * 1107 | * @param {HTMLElement} stack The vertical stack element 1108 | * @param {int} v Index to memorize 1109 | */ 1110 | function setPreviousVerticalIndex( stack, v ) { 1111 | 1112 | if( typeof stack === 'object' && typeof stack.setAttribute === 'function' ) { 1113 | stack.setAttribute( 'data-previous-indexv', v || 0 ); 1114 | } 1115 | 1116 | } 1117 | 1118 | /** 1119 | * Retrieves the vertical index which was stored using 1120 | * #setPreviousVerticalIndex() or 0 if no previous index 1121 | * exists. 1122 | * 1123 | * @param {HTMLElement} stack The vertical stack element 1124 | */ 1125 | function getPreviousVerticalIndex( stack ) { 1126 | 1127 | if( typeof stack === 'object' && typeof stack.setAttribute === 'function' && stack.classList.contains( 'stack' ) ) { 1128 | // Prefer manually defined start-indexv 1129 | var attributeName = stack.hasAttribute( 'data-start-indexv' ) ? 'data-start-indexv' : 'data-previous-indexv'; 1130 | 1131 | return parseInt( stack.getAttribute( attributeName ) || 0, 10 ); 1132 | } 1133 | 1134 | return 0; 1135 | 1136 | } 1137 | 1138 | /** 1139 | * Displays the overview of slides (quick nav) by 1140 | * scaling down and arranging all slide elements. 1141 | * 1142 | * Experimental feature, might be dropped if perf 1143 | * can't be improved. 1144 | */ 1145 | function activateOverview() { 1146 | 1147 | // Only proceed if enabled in config 1148 | if( config.overview ) { 1149 | 1150 | // Don't auto-slide while in overview mode 1151 | cancelAutoSlide(); 1152 | 1153 | var wasActive = dom.wrapper.classList.contains( 'overview' ); 1154 | 1155 | // Vary the depth of the overview based on screen size 1156 | var depth = window.innerWidth < 400 ? 1000 : 2500; 1157 | 1158 | dom.wrapper.classList.add( 'overview' ); 1159 | dom.wrapper.classList.remove( 'exit-overview' ); 1160 | 1161 | clearTimeout( activateOverviewTimeout ); 1162 | clearTimeout( deactivateOverviewTimeout ); 1163 | 1164 | // Not the pretties solution, but need to let the overview 1165 | // class apply first so that slides are measured accurately 1166 | // before we can position them 1167 | activateOverviewTimeout = setTimeout( function(){ 1168 | 1169 | var horizontalSlides = document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ); 1170 | 1171 | for( var i = 0, len1 = horizontalSlides.length; i < len1; i++ ) { 1172 | var hslide = horizontalSlides[i], 1173 | hoffset = config.rtl ? -105 : 105; 1174 | 1175 | hslide.setAttribute( 'data-index-h', i ); 1176 | 1177 | // Apply CSS transform 1178 | transformElement( hslide, 'translateZ(-'+ depth +'px) translate(' + ( ( i - indexh ) * hoffset ) + '%, 0%)' ); 1179 | 1180 | if( hslide.classList.contains( 'stack' ) ) { 1181 | 1182 | var verticalSlides = hslide.querySelectorAll( 'section' ); 1183 | 1184 | for( var j = 0, len2 = verticalSlides.length; j < len2; j++ ) { 1185 | var verticalIndex = i === indexh ? indexv : getPreviousVerticalIndex( hslide ); 1186 | 1187 | var vslide = verticalSlides[j]; 1188 | 1189 | vslide.setAttribute( 'data-index-h', i ); 1190 | vslide.setAttribute( 'data-index-v', j ); 1191 | 1192 | // Apply CSS transform 1193 | transformElement( vslide, 'translate(0%, ' + ( ( j - verticalIndex ) * 105 ) + '%)' ); 1194 | 1195 | // Navigate to this slide on click 1196 | vslide.addEventListener( 'click', onOverviewSlideClicked, true ); 1197 | } 1198 | 1199 | } 1200 | else { 1201 | 1202 | // Navigate to this slide on click 1203 | hslide.addEventListener( 'click', onOverviewSlideClicked, true ); 1204 | 1205 | } 1206 | } 1207 | 1208 | updateSlidesVisibility(); 1209 | 1210 | layout(); 1211 | 1212 | if( !wasActive ) { 1213 | // Notify observers of the overview showing 1214 | dispatchEvent( 'overviewshown', { 1215 | 'indexh': indexh, 1216 | 'indexv': indexv, 1217 | 'currentSlide': currentSlide 1218 | } ); 1219 | } 1220 | 1221 | }, 10 ); 1222 | 1223 | } 1224 | 1225 | } 1226 | 1227 | /** 1228 | * Exits the slide overview and enters the currently 1229 | * active slide. 1230 | */ 1231 | function deactivateOverview() { 1232 | 1233 | // Only proceed if enabled in config 1234 | if( config.overview ) { 1235 | 1236 | clearTimeout( activateOverviewTimeout ); 1237 | clearTimeout( deactivateOverviewTimeout ); 1238 | 1239 | dom.wrapper.classList.remove( 'overview' ); 1240 | 1241 | // Temporarily add a class so that transitions can do different things 1242 | // depending on whether they are exiting/entering overview, or just 1243 | // moving from slide to slide 1244 | dom.wrapper.classList.add( 'exit-overview' ); 1245 | 1246 | deactivateOverviewTimeout = setTimeout( function () { 1247 | dom.wrapper.classList.remove( 'exit-overview' ); 1248 | }, 10); 1249 | 1250 | // Select all slides 1251 | var slides = toArray( document.querySelectorAll( SLIDES_SELECTOR ) ); 1252 | 1253 | for( var i = 0, len = slides.length; i < len; i++ ) { 1254 | var element = slides[i]; 1255 | 1256 | element.style.display = ''; 1257 | 1258 | // Resets all transforms to use the external styles 1259 | transformElement( element, '' ); 1260 | 1261 | element.removeEventListener( 'click', onOverviewSlideClicked, true ); 1262 | } 1263 | 1264 | slide( indexh, indexv ); 1265 | 1266 | cueAutoSlide(); 1267 | 1268 | // Notify observers of the overview hiding 1269 | dispatchEvent( 'overviewhidden', { 1270 | 'indexh': indexh, 1271 | 'indexv': indexv, 1272 | 'currentSlide': currentSlide 1273 | } ); 1274 | 1275 | } 1276 | } 1277 | 1278 | /** 1279 | * Toggles the slide overview mode on and off. 1280 | * 1281 | * @param {Boolean} override Optional flag which overrides the 1282 | * toggle logic and forcibly sets the desired state. True means 1283 | * overview is open, false means it's closed. 1284 | */ 1285 | function toggleOverview( override ) { 1286 | 1287 | if( typeof override === 'boolean' ) { 1288 | override ? activateOverview() : deactivateOverview(); 1289 | } 1290 | else { 1291 | isOverview() ? deactivateOverview() : activateOverview(); 1292 | } 1293 | 1294 | } 1295 | 1296 | /** 1297 | * Checks if the overview is currently active. 1298 | * 1299 | * @return {Boolean} true if the overview is active, 1300 | * false otherwise 1301 | */ 1302 | function isOverview() { 1303 | 1304 | return dom.wrapper.classList.contains( 'overview' ); 1305 | 1306 | } 1307 | 1308 | /** 1309 | * Checks if the current or specified slide is vertical 1310 | * (nested within another slide). 1311 | * 1312 | * @param {HTMLElement} slide [optional] The slide to check 1313 | * orientation of 1314 | */ 1315 | function isVerticalSlide( slide ) { 1316 | 1317 | // Prefer slide argument, otherwise use current slide 1318 | slide = slide ? slide : currentSlide; 1319 | 1320 | return slide && !!slide.parentNode.nodeName.match( /section/i ); 1321 | 1322 | } 1323 | 1324 | /** 1325 | * Handling the fullscreen functionality via the fullscreen API 1326 | * 1327 | * @see http://fullscreen.spec.whatwg.org/ 1328 | * @see https://developer.mozilla.org/en-US/docs/DOM/Using_fullscreen_mode 1329 | */ 1330 | function enterFullscreen() { 1331 | 1332 | var element = document.body; 1333 | 1334 | // Check which implementation is available 1335 | var requestMethod = element.requestFullScreen || 1336 | element.webkitRequestFullscreen || 1337 | element.webkitRequestFullScreen || 1338 | element.mozRequestFullScreen || 1339 | element.msRequestFullScreen; 1340 | 1341 | if( requestMethod ) { 1342 | requestMethod.apply( element ); 1343 | } 1344 | 1345 | } 1346 | 1347 | /** 1348 | * Enters the paused mode which fades everything on screen to 1349 | * black. 1350 | */ 1351 | function pause() { 1352 | 1353 | var wasPaused = dom.wrapper.classList.contains( 'paused' ); 1354 | 1355 | cancelAutoSlide(); 1356 | dom.wrapper.classList.add( 'paused' ); 1357 | 1358 | if( wasPaused === false ) { 1359 | dispatchEvent( 'paused' ); 1360 | } 1361 | 1362 | } 1363 | 1364 | /** 1365 | * Exits from the paused mode. 1366 | */ 1367 | function resume() { 1368 | 1369 | var wasPaused = dom.wrapper.classList.contains( 'paused' ); 1370 | dom.wrapper.classList.remove( 'paused' ); 1371 | 1372 | cueAutoSlide(); 1373 | 1374 | if( wasPaused ) { 1375 | dispatchEvent( 'resumed' ); 1376 | } 1377 | 1378 | } 1379 | 1380 | /** 1381 | * Toggles the paused mode on and off. 1382 | */ 1383 | function togglePause() { 1384 | 1385 | if( isPaused() ) { 1386 | resume(); 1387 | } 1388 | else { 1389 | pause(); 1390 | } 1391 | 1392 | } 1393 | 1394 | /** 1395 | * Checks if we are currently in the paused mode. 1396 | */ 1397 | function isPaused() { 1398 | 1399 | return dom.wrapper.classList.contains( 'paused' ); 1400 | 1401 | } 1402 | 1403 | /** 1404 | * Steps from the current point in the presentation to the 1405 | * slide which matches the specified horizontal and vertical 1406 | * indices. 1407 | * 1408 | * @param {int} h Horizontal index of the target slide 1409 | * @param {int} v Vertical index of the target slide 1410 | * @param {int} f Optional index of a fragment within the 1411 | * target slide to activate 1412 | * @param {int} o Optional origin for use in multimaster environments 1413 | */ 1414 | function slide( h, v, f, o ) { 1415 | 1416 | // Remember where we were at before 1417 | previousSlide = currentSlide; 1418 | 1419 | // Query all horizontal slides in the deck 1420 | var horizontalSlides = document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ); 1421 | 1422 | // If no vertical index is specified and the upcoming slide is a 1423 | // stack, resume at its previous vertical index 1424 | if( v === undefined ) { 1425 | v = getPreviousVerticalIndex( horizontalSlides[ h ] ); 1426 | } 1427 | 1428 | // If we were on a vertical stack, remember what vertical index 1429 | // it was on so we can resume at the same position when returning 1430 | if( previousSlide && previousSlide.parentNode && previousSlide.parentNode.classList.contains( 'stack' ) ) { 1431 | setPreviousVerticalIndex( previousSlide.parentNode, indexv ); 1432 | } 1433 | 1434 | // Remember the state before this slide 1435 | var stateBefore = state.concat(); 1436 | 1437 | // Reset the state array 1438 | state.length = 0; 1439 | 1440 | var indexhBefore = indexh || 0, 1441 | indexvBefore = indexv || 0; 1442 | 1443 | // Activate and transition to the new slide 1444 | indexh = updateSlides( HORIZONTAL_SLIDES_SELECTOR, h === undefined ? indexh : h ); 1445 | indexv = updateSlides( VERTICAL_SLIDES_SELECTOR, v === undefined ? indexv : v ); 1446 | 1447 | // Update the visibility of slides now that the indices have changed 1448 | updateSlidesVisibility(); 1449 | 1450 | layout(); 1451 | 1452 | // Apply the new state 1453 | stateLoop: for( var i = 0, len = state.length; i < len; i++ ) { 1454 | // Check if this state existed on the previous slide. If it 1455 | // did, we will avoid adding it repeatedly 1456 | for( var j = 0; j < stateBefore.length; j++ ) { 1457 | if( stateBefore[j] === state[i] ) { 1458 | stateBefore.splice( j, 1 ); 1459 | continue stateLoop; 1460 | } 1461 | } 1462 | 1463 | document.documentElement.classList.add( state[i] ); 1464 | 1465 | // Dispatch custom event matching the state's name 1466 | dispatchEvent( state[i] ); 1467 | } 1468 | 1469 | // Clean up the remains of the previous state 1470 | while( stateBefore.length ) { 1471 | document.documentElement.classList.remove( stateBefore.pop() ); 1472 | } 1473 | 1474 | // If the overview is active, re-activate it to update positions 1475 | if( isOverview() ) { 1476 | activateOverview(); 1477 | } 1478 | 1479 | // Find the current horizontal slide and any possible vertical slides 1480 | // within it 1481 | var currentHorizontalSlide = horizontalSlides[ indexh ], 1482 | currentVerticalSlides = currentHorizontalSlide.querySelectorAll( 'section' ); 1483 | 1484 | // Store references to the previous and current slides 1485 | currentSlide = currentVerticalSlides[ indexv ] || currentHorizontalSlide; 1486 | 1487 | 1488 | // Show fragment, if specified 1489 | if( typeof f !== 'undefined' ) { 1490 | var fragments = sortFragments( currentSlide.querySelectorAll( '.fragment' ) ); 1491 | 1492 | toArray( fragments ).forEach( function( fragment, indexf ) { 1493 | if( indexf < f ) { 1494 | fragment.classList.add( 'visible' ); 1495 | } 1496 | else { 1497 | fragment.classList.remove( 'visible' ); 1498 | } 1499 | } ); 1500 | } 1501 | 1502 | // Dispatch an event if the slide changed 1503 | var slideChanged = ( indexh !== indexhBefore || indexv !== indexvBefore ); 1504 | if( slideChanged ) { 1505 | dispatchEvent( 'slidechanged', { 1506 | 'indexh': indexh, 1507 | 'indexv': indexv, 1508 | 'previousSlide': previousSlide, 1509 | 'currentSlide': currentSlide, 1510 | 'origin': o 1511 | } ); 1512 | } 1513 | else { 1514 | // Ensure that the previous slide is never the same as the current 1515 | previousSlide = null; 1516 | } 1517 | 1518 | // Solves an edge case where the previous slide maintains the 1519 | // 'present' class when navigating between adjacent vertical 1520 | // stacks 1521 | if( previousSlide ) { 1522 | previousSlide.classList.remove( 'present' ); 1523 | 1524 | // Reset all slides upon navigate to home 1525 | // Issue: #285 1526 | if ( document.querySelector( HOME_SLIDE_SELECTOR ).classList.contains( 'present' ) ) { 1527 | // Launch async task 1528 | setTimeout( function () { 1529 | var slides = toArray( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR + '.stack') ), i; 1530 | for( i in slides ) { 1531 | if( slides[i] ) { 1532 | // Reset stack 1533 | setPreviousVerticalIndex( slides[i], 0 ); 1534 | } 1535 | } 1536 | }, 0 ); 1537 | } 1538 | } 1539 | 1540 | // Handle embedded content 1541 | if( slideChanged ) { 1542 | stopEmbeddedContent( previousSlide ); 1543 | startEmbeddedContent( currentSlide ); 1544 | } 1545 | 1546 | updateControls(); 1547 | updateProgress(); 1548 | updateBackground(); 1549 | 1550 | // Update the URL hash 1551 | writeURL(); 1552 | 1553 | } 1554 | 1555 | /** 1556 | * Syncs the presentation with the current DOM. Useful 1557 | * when new slides or control elements are added or when 1558 | * the configuration has changed. 1559 | */ 1560 | function sync() { 1561 | 1562 | // Subscribe to input 1563 | removeEventListeners(); 1564 | addEventListeners(); 1565 | 1566 | // Force a layout to make sure the current config is accounted for 1567 | layout(); 1568 | 1569 | // Reflect the current autoSlide value 1570 | autoSlide = config.autoSlide; 1571 | 1572 | // Start auto-sliding if it's enabled 1573 | cueAutoSlide(); 1574 | 1575 | // Re-create the slide backgrounds 1576 | createBackgrounds(); 1577 | 1578 | updateControls(); 1579 | updateProgress(); 1580 | updateBackground(); 1581 | 1582 | } 1583 | 1584 | /** 1585 | * Updates one dimension of slides by showing the slide 1586 | * with the specified index. 1587 | * 1588 | * @param {String} selector A CSS selector that will fetch 1589 | * the group of slides we are working with 1590 | * @param {Number} index The index of the slide that should be 1591 | * shown 1592 | * 1593 | * @return {Number} The index of the slide that is now shown, 1594 | * might differ from the passed in index if it was out of 1595 | * bounds. 1596 | */ 1597 | function updateSlides( selector, index ) { 1598 | 1599 | // Select all slides and convert the NodeList result to 1600 | // an array 1601 | var slides = toArray( document.querySelectorAll( selector ) ), 1602 | slidesLength = slides.length; 1603 | 1604 | if( slidesLength ) { 1605 | 1606 | // Should the index loop? 1607 | if( config.loop ) { 1608 | index %= slidesLength; 1609 | 1610 | if( index < 0 ) { 1611 | index = slidesLength + index; 1612 | } 1613 | } 1614 | 1615 | // Enforce max and minimum index bounds 1616 | index = Math.max( Math.min( index, slidesLength - 1 ), 0 ); 1617 | 1618 | for( var i = 0; i < slidesLength; i++ ) { 1619 | var element = slides[i]; 1620 | 1621 | var reverse = config.rtl && !isVerticalSlide( element ); 1622 | 1623 | element.classList.remove( 'past' ); 1624 | element.classList.remove( 'present' ); 1625 | element.classList.remove( 'future' ); 1626 | 1627 | // http://www.w3.org/html/wg/drafts/html/master/editing.html#the-hidden-attribute 1628 | element.setAttribute( 'hidden', '' ); 1629 | 1630 | if( i < index ) { 1631 | // Any element previous to index is given the 'past' class 1632 | element.classList.add( reverse ? 'future' : 'past' ); 1633 | } 1634 | else if( i > index ) { 1635 | // Any element subsequent to index is given the 'future' class 1636 | element.classList.add( reverse ? 'past' : 'future' ); 1637 | 1638 | var fragments = toArray( element.querySelectorAll( '.fragment.visible' ) ); 1639 | 1640 | // No fragments in future slides should be visible ahead of time 1641 | while( fragments.length ) { 1642 | fragments.pop().classList.remove( 'visible' ); 1643 | } 1644 | } 1645 | 1646 | // If this element contains vertical slides 1647 | if( element.querySelector( 'section' ) ) { 1648 | element.classList.add( 'stack' ); 1649 | } 1650 | } 1651 | 1652 | // Mark the current slide as present 1653 | slides[index].classList.add( 'present' ); 1654 | slides[index].removeAttribute( 'hidden' ); 1655 | 1656 | // If this slide has a state associated with it, add it 1657 | // onto the current state of the deck 1658 | var slideState = slides[index].getAttribute( 'data-state' ); 1659 | if( slideState ) { 1660 | state = state.concat( slideState.split( ' ' ) ); 1661 | } 1662 | 1663 | // If this slide has a data-autoslide attribute associated use this as 1664 | // autoSlide value otherwise use the global configured time 1665 | var slideAutoSlide = slides[index].getAttribute( 'data-autoslide' ); 1666 | if( slideAutoSlide ) { 1667 | autoSlide = parseInt( slideAutoSlide, 10 ); 1668 | } 1669 | else { 1670 | autoSlide = config.autoSlide; 1671 | } 1672 | 1673 | cueAutoSlide(); 1674 | 1675 | } 1676 | else { 1677 | // Since there are no slides we can't be anywhere beyond the 1678 | // zeroth index 1679 | index = 0; 1680 | } 1681 | 1682 | return index; 1683 | 1684 | } 1685 | 1686 | /** 1687 | * Optimization method; hide all slides that are far away 1688 | * from the present slide. 1689 | */ 1690 | function updateSlidesVisibility() { 1691 | 1692 | // Select all slides and convert the NodeList result to 1693 | // an array 1694 | var horizontalSlides = toArray( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ), 1695 | horizontalSlidesLength = horizontalSlides.length, 1696 | distanceX, 1697 | distanceY; 1698 | 1699 | if( horizontalSlidesLength ) { 1700 | 1701 | // The number of steps away from the present slide that will 1702 | // be visible 1703 | var viewDistance = isOverview() ? 10 : config.viewDistance; 1704 | 1705 | // Limit view distance on weaker devices 1706 | if( isMobileDevice ) { 1707 | viewDistance = isOverview() ? 6 : 1; 1708 | } 1709 | 1710 | for( var x = 0; x < horizontalSlidesLength; x++ ) { 1711 | var horizontalSlide = horizontalSlides[x]; 1712 | 1713 | var verticalSlides = toArray( horizontalSlide.querySelectorAll( 'section' ) ), 1714 | verticalSlidesLength = verticalSlides.length; 1715 | 1716 | // Loops so that it measures 1 between the first and last slides 1717 | distanceX = Math.abs( ( indexh - x ) % ( horizontalSlidesLength - viewDistance ) ) || 0; 1718 | 1719 | // Show the horizontal slide if it's within the view distance 1720 | horizontalSlide.style.display = distanceX > viewDistance ? 'none' : 'block'; 1721 | 1722 | if( verticalSlidesLength ) { 1723 | 1724 | var oy = getPreviousVerticalIndex( horizontalSlide ); 1725 | 1726 | for( var y = 0; y < verticalSlidesLength; y++ ) { 1727 | var verticalSlide = verticalSlides[y]; 1728 | 1729 | distanceY = x === indexh ? Math.abs( indexv - y ) : Math.abs( y - oy ); 1730 | 1731 | verticalSlide.style.display = ( distanceX + distanceY ) > viewDistance ? 'none' : 'block'; 1732 | } 1733 | 1734 | } 1735 | } 1736 | 1737 | } 1738 | 1739 | } 1740 | 1741 | /** 1742 | * Updates the progress bar to reflect the current slide. 1743 | */ 1744 | function updateProgress() { 1745 | 1746 | // Update progress if enabled 1747 | if( config.progress && dom.progress ) { 1748 | 1749 | var horizontalSlides = toArray( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ); 1750 | 1751 | // The number of past and total slides 1752 | var totalCount = document.querySelectorAll( SLIDES_SELECTOR + ':not(.stack)' ).length; 1753 | var pastCount = 0; 1754 | 1755 | // Step through all slides and count the past ones 1756 | mainLoop: for( var i = 0; i < horizontalSlides.length; i++ ) { 1757 | 1758 | var horizontalSlide = horizontalSlides[i]; 1759 | var verticalSlides = toArray( horizontalSlide.querySelectorAll( 'section' ) ); 1760 | 1761 | for( var j = 0; j < verticalSlides.length; j++ ) { 1762 | 1763 | // Stop as soon as we arrive at the present 1764 | if( verticalSlides[j].classList.contains( 'present' ) ) { 1765 | break mainLoop; 1766 | } 1767 | 1768 | pastCount++; 1769 | 1770 | } 1771 | 1772 | // Stop as soon as we arrive at the present 1773 | if( horizontalSlide.classList.contains( 'present' ) ) { 1774 | break; 1775 | } 1776 | 1777 | // Don't count the wrapping section for vertical slides 1778 | if( horizontalSlide.classList.contains( 'stack' ) === false ) { 1779 | pastCount++; 1780 | } 1781 | 1782 | } 1783 | 1784 | dom.progressbar.style.width = ( pastCount / ( totalCount - 1 ) ) * window.innerWidth + 'px'; 1785 | 1786 | } 1787 | 1788 | } 1789 | 1790 | /** 1791 | * Updates the state of all control/navigation arrows. 1792 | */ 1793 | function updateControls() { 1794 | 1795 | if ( config.controls && dom.controls ) { 1796 | 1797 | var routes = availableRoutes(); 1798 | var fragments = availableFragments(); 1799 | 1800 | // Remove the 'enabled' class from all directions 1801 | dom.controlsLeft.concat( dom.controlsRight ) 1802 | .concat( dom.controlsUp ) 1803 | .concat( dom.controlsDown ) 1804 | .concat( dom.controlsPrev ) 1805 | .concat( dom.controlsNext ).forEach( function( node ) { 1806 | node.classList.remove( 'enabled' ); 1807 | node.classList.remove( 'fragmented' ); 1808 | } ); 1809 | 1810 | // Add the 'enabled' class to the available routes 1811 | if( routes.left ) dom.controlsLeft.forEach( function( el ) { el.classList.add( 'enabled' ); } ); 1812 | if( routes.right ) dom.controlsRight.forEach( function( el ) { el.classList.add( 'enabled' ); } ); 1813 | if( routes.up ) dom.controlsUp.forEach( function( el ) { el.classList.add( 'enabled' ); } ); 1814 | if( routes.down ) dom.controlsDown.forEach( function( el ) { el.classList.add( 'enabled' ); } ); 1815 | 1816 | // Prev/next buttons 1817 | if( routes.left || routes.up ) dom.controlsPrev.forEach( function( el ) { el.classList.add( 'enabled' ); } ); 1818 | if( routes.right || routes.down ) dom.controlsNext.forEach( function( el ) { el.classList.add( 'enabled' ); } ); 1819 | 1820 | // Highlight fragment directions 1821 | if( currentSlide ) { 1822 | 1823 | // Always apply fragment decorator to prev/next buttons 1824 | if( fragments.prev ) dom.controlsPrev.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } ); 1825 | if( fragments.next ) dom.controlsNext.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } ); 1826 | 1827 | // Apply fragment decorators to directional buttons based on 1828 | // what slide axis they are in 1829 | if( isVerticalSlide( currentSlide ) ) { 1830 | if( fragments.prev ) dom.controlsUp.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } ); 1831 | if( fragments.next ) dom.controlsDown.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } ); 1832 | } 1833 | else { 1834 | if( fragments.prev ) dom.controlsLeft.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } ); 1835 | if( fragments.next ) dom.controlsRight.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } ); 1836 | } 1837 | } 1838 | 1839 | } 1840 | 1841 | } 1842 | 1843 | /** 1844 | * Updates the background elements to reflect the current 1845 | * slide. 1846 | */ 1847 | function updateBackground() { 1848 | 1849 | // Update the classes of all backgrounds to match the 1850 | // states of their slides (past/present/future) 1851 | toArray( dom.background.childNodes ).forEach( function( backgroundh, h ) { 1852 | 1853 | // Reverse past/future classes when in RTL mode 1854 | var horizontalPast = config.rtl ? 'future' : 'past', 1855 | horizontalFuture = config.rtl ? 'past' : 'future'; 1856 | 1857 | backgroundh.className = 'slide-background ' + ( h < indexh ? horizontalPast : h > indexh ? horizontalFuture : 'present' ); 1858 | 1859 | toArray( backgroundh.childNodes ).forEach( function( backgroundv, v ) { 1860 | 1861 | backgroundv.className = 'slide-background ' + ( v < indexv ? 'past' : v > indexv ? 'future' : 'present' ); 1862 | 1863 | } ); 1864 | 1865 | } ); 1866 | 1867 | // Allow the first background to apply without transition 1868 | setTimeout( function() { 1869 | dom.background.classList.remove( 'no-transition' ); 1870 | }, 1 ); 1871 | 1872 | } 1873 | 1874 | /** 1875 | * Determine what available routes there are for navigation. 1876 | * 1877 | * @return {Object} containing four booleans: left/right/up/down 1878 | */ 1879 | function availableRoutes() { 1880 | 1881 | var horizontalSlides = document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ), 1882 | verticalSlides = document.querySelectorAll( VERTICAL_SLIDES_SELECTOR ); 1883 | 1884 | var routes = { 1885 | left: indexh > 0 || config.loop, 1886 | right: indexh < horizontalSlides.length - 1 || config.loop, 1887 | up: indexv > 0, 1888 | down: indexv < verticalSlides.length - 1 1889 | }; 1890 | 1891 | // reverse horizontal controls for rtl 1892 | if( config.rtl ) { 1893 | var left = routes.left; 1894 | routes.left = routes.right; 1895 | routes.right = left; 1896 | } 1897 | 1898 | return routes; 1899 | 1900 | } 1901 | 1902 | /** 1903 | * Returns an object describing the available fragment 1904 | * directions. 1905 | * 1906 | * @return {Object} two boolean properties: prev/next 1907 | */ 1908 | function availableFragments() { 1909 | 1910 | if( currentSlide && config.fragments ) { 1911 | var fragments = currentSlide.querySelectorAll( '.fragment' ); 1912 | var hiddenFragments = currentSlide.querySelectorAll( '.fragment:not(.visible)' ); 1913 | 1914 | return { 1915 | prev: fragments.length - hiddenFragments.length > 0, 1916 | next: !!hiddenFragments.length 1917 | }; 1918 | } 1919 | else { 1920 | return { prev: false, next: false }; 1921 | } 1922 | 1923 | } 1924 | 1925 | /** 1926 | * Start playback of any embedded content inside of 1927 | * the targeted slide. 1928 | */ 1929 | function startEmbeddedContent( slide ) { 1930 | 1931 | if( slide ) { 1932 | // HTML5 media elements 1933 | toArray( slide.querySelectorAll( 'video, audio' ) ).forEach( function( el ) { 1934 | if( el.hasAttribute( 'data-autoplay' ) ) { 1935 | el.play(); 1936 | } 1937 | } ); 1938 | 1939 | // YouTube embeds 1940 | toArray( slide.querySelectorAll( 'iframe[src*="youtube.com/embed/"]' ) ).forEach( function( el ) { 1941 | if( el.hasAttribute( 'data-autoplay' ) ) { 1942 | el.contentWindow.postMessage('{"event":"command","func":"playVideo","args":""}', '*'); 1943 | } 1944 | }); 1945 | } 1946 | 1947 | } 1948 | 1949 | /** 1950 | * Stop playback of any embedded content inside of 1951 | * the targeted slide. 1952 | */ 1953 | function stopEmbeddedContent( slide ) { 1954 | 1955 | if( slide ) { 1956 | // HTML5 media elements 1957 | toArray( slide.querySelectorAll( 'video, audio' ) ).forEach( function( el ) { 1958 | if( !el.hasAttribute( 'data-ignore' ) ) { 1959 | el.pause(); 1960 | } 1961 | } ); 1962 | 1963 | // YouTube embeds 1964 | toArray( slide.querySelectorAll( 'iframe[src*="youtube.com/embed/"]' ) ).forEach( function( el ) { 1965 | if( !el.hasAttribute( 'data-ignore' ) && typeof el.contentWindow.postMessage === 'function' ) { 1966 | el.contentWindow.postMessage('{"event":"command","func":"pauseVideo","args":""}', '*'); 1967 | } 1968 | }); 1969 | } 1970 | 1971 | } 1972 | 1973 | /** 1974 | * Reads the current URL (hash) and navigates accordingly. 1975 | */ 1976 | function readURL() { 1977 | 1978 | var hash = window.location.hash; 1979 | 1980 | // Attempt to parse the hash as either an index or name 1981 | var bits = hash.slice( 2 ).split( '/' ), 1982 | name = hash.replace( /#|\//gi, '' ); 1983 | 1984 | // If the first bit is invalid and there is a name we can 1985 | // assume that this is a named link 1986 | if( isNaN( parseInt( bits[0], 10 ) ) && name.length ) { 1987 | // Find the slide with the specified name 1988 | var element = document.querySelector( '#' + name ); 1989 | 1990 | if( element ) { 1991 | // Find the position of the named slide and navigate to it 1992 | var indices = Reveal.getIndices( element ); 1993 | slide( indices.h, indices.v ); 1994 | } 1995 | // If the slide doesn't exist, navigate to the current slide 1996 | else { 1997 | slide( indexh || 0, indexv || 0 ); 1998 | } 1999 | } 2000 | else { 2001 | // Read the index components of the hash 2002 | var h = parseInt( bits[0], 10 ) || 0, 2003 | v = parseInt( bits[1], 10 ) || 0; 2004 | 2005 | if( h !== indexh || v !== indexv ) { 2006 | slide( h, v ); 2007 | } 2008 | } 2009 | 2010 | } 2011 | 2012 | /** 2013 | * Updates the page URL (hash) to reflect the current 2014 | * state. 2015 | * 2016 | * @param {Number} delay The time in ms to wait before 2017 | * writing the hash 2018 | */ 2019 | function writeURL( delay ) { 2020 | 2021 | if( config.history ) { 2022 | 2023 | // Make sure there's never more than one timeout running 2024 | clearTimeout( writeURLTimeout ); 2025 | 2026 | // If a delay is specified, timeout this call 2027 | if( typeof delay === 'number' ) { 2028 | writeURLTimeout = setTimeout( writeURL, delay ); 2029 | } 2030 | else { 2031 | var url = '/'; 2032 | 2033 | // If the current slide has an ID, use that as a named link 2034 | if( currentSlide && typeof currentSlide.getAttribute( 'id' ) === 'string' ) { 2035 | url = '/' + currentSlide.getAttribute( 'id' ); 2036 | } 2037 | // Otherwise use the /h/v index 2038 | else { 2039 | if( indexh > 0 || indexv > 0 ) url += indexh; 2040 | if( indexv > 0 ) url += '/' + indexv; 2041 | } 2042 | 2043 | window.location.hash = url; 2044 | } 2045 | } 2046 | 2047 | } 2048 | 2049 | /** 2050 | * Retrieves the h/v location of the current, or specified, 2051 | * slide. 2052 | * 2053 | * @param {HTMLElement} slide If specified, the returned 2054 | * index will be for this slide rather than the currently 2055 | * active one 2056 | * 2057 | * @return {Object} { h: , v: , f: } 2058 | */ 2059 | function getIndices( slide ) { 2060 | 2061 | // By default, return the current indices 2062 | var h = indexh, 2063 | v = indexv, 2064 | f; 2065 | 2066 | // If a slide is specified, return the indices of that slide 2067 | if( slide ) { 2068 | var isVertical = isVerticalSlide( slide ); 2069 | var slideh = isVertical ? slide.parentNode : slide; 2070 | 2071 | // Select all horizontal slides 2072 | var horizontalSlides = toArray( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ); 2073 | 2074 | // Now that we know which the horizontal slide is, get its index 2075 | h = Math.max( horizontalSlides.indexOf( slideh ), 0 ); 2076 | 2077 | // If this is a vertical slide, grab the vertical index 2078 | if( isVertical ) { 2079 | v = Math.max( toArray( slide.parentNode.querySelectorAll( 'section' ) ).indexOf( slide ), 0 ); 2080 | } 2081 | } 2082 | 2083 | if( !slide && currentSlide ) { 2084 | var hasFragments = currentSlide.querySelectorAll( '.fragment' ).length > 0; 2085 | if( hasFragments ) { 2086 | var visibleFragments = currentSlide.querySelectorAll( '.fragment.visible' ); 2087 | f = visibleFragments.length; 2088 | } 2089 | } 2090 | 2091 | return { h: h, v: v, f: f }; 2092 | 2093 | } 2094 | 2095 | /** 2096 | * Navigate to the next slide fragment. 2097 | * 2098 | * @return {Boolean} true if there was a next fragment, 2099 | * false otherwise 2100 | */ 2101 | function nextFragment() { 2102 | 2103 | if( currentSlide && config.fragments ) { 2104 | var fragments = sortFragments( currentSlide.querySelectorAll( '.fragment:not(.visible)' ) ); 2105 | 2106 | if( fragments.length ) { 2107 | // Find the index of the next fragment 2108 | var index = fragments[0].getAttribute( 'data-fragment-index' ); 2109 | 2110 | // Find all fragments with the same index 2111 | fragments = currentSlide.querySelectorAll( '.fragment[data-fragment-index="'+ index +'"]' ); 2112 | 2113 | toArray( fragments ).forEach( function( element ) { 2114 | element.classList.add( 'visible' ); 2115 | } ); 2116 | 2117 | // Notify subscribers of the change 2118 | dispatchEvent( 'fragmentshown', { fragment: fragments[0], fragments: fragments } ); 2119 | 2120 | updateControls(); 2121 | return true; 2122 | } 2123 | } 2124 | 2125 | return false; 2126 | 2127 | } 2128 | 2129 | /** 2130 | * Navigate to the previous slide fragment. 2131 | * 2132 | * @return {Boolean} true if there was a previous fragment, 2133 | * false otherwise 2134 | */ 2135 | function previousFragment() { 2136 | 2137 | if( currentSlide && config.fragments ) { 2138 | var fragments = sortFragments( currentSlide.querySelectorAll( '.fragment.visible' ) ); 2139 | 2140 | if( fragments.length ) { 2141 | // Find the index of the previous fragment 2142 | var index = fragments[ fragments.length - 1 ].getAttribute( 'data-fragment-index' ); 2143 | 2144 | // Find all fragments with the same index 2145 | fragments = currentSlide.querySelectorAll( '.fragment[data-fragment-index="'+ index +'"]' ); 2146 | 2147 | toArray( fragments ).forEach( function( f ) { 2148 | f.classList.remove( 'visible' ); 2149 | } ); 2150 | 2151 | // Notify subscribers of the change 2152 | dispatchEvent( 'fragmenthidden', { fragment: fragments[0], fragments: fragments } ); 2153 | 2154 | updateControls(); 2155 | return true; 2156 | } 2157 | } 2158 | 2159 | return false; 2160 | 2161 | } 2162 | 2163 | /** 2164 | * Cues a new automated slide if enabled in the config. 2165 | */ 2166 | function cueAutoSlide() { 2167 | 2168 | clearTimeout( autoSlideTimeout ); 2169 | 2170 | // Cue the next auto-slide if enabled 2171 | if( autoSlide && !isPaused() && !isOverview() ) { 2172 | autoSlideTimeout = setTimeout( navigateNext, autoSlide ); 2173 | } 2174 | 2175 | } 2176 | 2177 | /** 2178 | * Cancels any ongoing request to auto-slide. 2179 | */ 2180 | function cancelAutoSlide() { 2181 | 2182 | clearTimeout( autoSlideTimeout ); 2183 | 2184 | } 2185 | 2186 | function navigateLeft() { 2187 | 2188 | // Reverse for RTL 2189 | if( config.rtl ) { 2190 | if( ( isOverview() || nextFragment() === false ) && availableRoutes().left ) { 2191 | slide( indexh + 1 ); 2192 | } 2193 | } 2194 | // Normal navigation 2195 | else if( ( isOverview() || previousFragment() === false ) && availableRoutes().left ) { 2196 | slide( indexh - 1 ); 2197 | } 2198 | 2199 | } 2200 | 2201 | function navigateRight() { 2202 | 2203 | // Reverse for RTL 2204 | if( config.rtl ) { 2205 | if( ( isOverview() || previousFragment() === false ) && availableRoutes().right ) { 2206 | slide( indexh - 1 ); 2207 | } 2208 | } 2209 | // Normal navigation 2210 | else if( ( isOverview() || nextFragment() === false ) && availableRoutes().right ) { 2211 | slide( indexh + 1 ); 2212 | } 2213 | 2214 | } 2215 | 2216 | function navigateUp() { 2217 | 2218 | // Prioritize hiding fragments 2219 | if( ( isOverview() || previousFragment() === false ) && availableRoutes().up ) { 2220 | slide( indexh, indexv - 1 ); 2221 | } 2222 | 2223 | } 2224 | 2225 | function navigateDown() { 2226 | 2227 | // Prioritize revealing fragments 2228 | if( ( isOverview() || nextFragment() === false ) && availableRoutes().down ) { 2229 | slide( indexh, indexv + 1 ); 2230 | } 2231 | 2232 | } 2233 | 2234 | /** 2235 | * Navigates backwards, prioritized in the following order: 2236 | * 1) Previous fragment 2237 | * 2) Previous vertical slide 2238 | * 3) Previous horizontal slide 2239 | */ 2240 | function navigatePrev() { 2241 | 2242 | // Prioritize revealing fragments 2243 | if( previousFragment() === false ) { 2244 | if( availableRoutes().up ) { 2245 | navigateUp(); 2246 | } 2247 | else { 2248 | // Fetch the previous horizontal slide, if there is one 2249 | var previousSlide = document.querySelector( HORIZONTAL_SLIDES_SELECTOR + '.past:nth-child(' + indexh + ')' ); 2250 | 2251 | if( previousSlide ) { 2252 | var v = ( previousSlide.querySelectorAll( 'section' ).length - 1 ) || undefined; 2253 | var h = indexh - 1; 2254 | slide( h, v ); 2255 | } 2256 | } 2257 | } 2258 | 2259 | } 2260 | 2261 | /** 2262 | * Same as #navigatePrev() but navigates forwards. 2263 | */ 2264 | function navigateNext() { 2265 | 2266 | // Prioritize revealing fragments 2267 | if( nextFragment() === false ) { 2268 | availableRoutes().down ? navigateDown() : navigateRight(); 2269 | } 2270 | 2271 | // If auto-sliding is enabled we need to cue up 2272 | // another timeout 2273 | cueAutoSlide(); 2274 | 2275 | } 2276 | 2277 | 2278 | // --------------------------------------------------------------------// 2279 | // ----------------------------- EVENTS -------------------------------// 2280 | // --------------------------------------------------------------------// 2281 | 2282 | 2283 | /** 2284 | * Handler for the document level 'keydown' event. 2285 | * 2286 | * @param {Object} event 2287 | */ 2288 | function onDocumentKeyDown( event ) { 2289 | 2290 | // Check if there's a focused element that could be using 2291 | // the keyboard 2292 | var activeElement = document.activeElement; 2293 | var hasFocus = !!( document.activeElement && ( document.activeElement.type || document.activeElement.href || document.activeElement.contentEditable !== 'inherit' ) ); 2294 | 2295 | // Disregard the event if there's a focused element or a 2296 | // keyboard modifier key is present 2297 | if( hasFocus || (event.shiftKey && event.keyCode !== 32) || event.altKey || event.ctrlKey || event.metaKey ) return; 2298 | 2299 | // While paused only allow "unpausing" keyboard events (b and .) 2300 | if( isPaused() && [66,190,191].indexOf( event.keyCode ) === -1 ) { 2301 | return false; 2302 | } 2303 | 2304 | var triggered = false; 2305 | 2306 | // 1. User defined key bindings 2307 | if( typeof config.keyboard === 'object' ) { 2308 | 2309 | for( var key in config.keyboard ) { 2310 | 2311 | // Check if this binding matches the pressed key 2312 | if( parseInt( key, 10 ) === event.keyCode ) { 2313 | 2314 | var value = config.keyboard[ key ]; 2315 | 2316 | // Callback function 2317 | if( typeof value === 'function' ) { 2318 | value.apply( null, [ event ] ); 2319 | } 2320 | // String shortcuts to reveal.js API 2321 | else if( typeof value === 'string' && typeof Reveal[ value ] === 'function' ) { 2322 | Reveal[ value ].call(); 2323 | } 2324 | 2325 | triggered = true; 2326 | 2327 | } 2328 | 2329 | } 2330 | 2331 | } 2332 | 2333 | // 2. System defined key bindings 2334 | if( triggered === false ) { 2335 | 2336 | // Assume true and try to prove false 2337 | triggered = true; 2338 | 2339 | switch( event.keyCode ) { 2340 | // p, page up 2341 | case 80: case 33: navigatePrev(); break; 2342 | // n, page down 2343 | case 78: case 34: navigateNext(); break; 2344 | // h, left 2345 | case 72: case 37: navigateLeft(); break; 2346 | // l, right 2347 | case 76: case 39: navigateRight(); break; 2348 | // k, up 2349 | case 75: case 38: navigateUp(); break; 2350 | // j, down 2351 | case 74: case 40: navigateDown(); break; 2352 | // home 2353 | case 36: slide( 0 ); break; 2354 | // end 2355 | case 35: slide( Number.MAX_VALUE ); break; 2356 | // space 2357 | case 32: isOverview() ? deactivateOverview() : event.shiftKey ? navigatePrev() : navigateNext(); break; 2358 | // return 2359 | case 13: isOverview() ? deactivateOverview() : triggered = false; break; 2360 | // b, period, Logitech presenter tools "black screen" button 2361 | case 66: case 190: case 191: togglePause(); break; 2362 | // f 2363 | case 70: enterFullscreen(); break; 2364 | default: 2365 | triggered = false; 2366 | } 2367 | 2368 | } 2369 | 2370 | // If the input resulted in a triggered action we should prevent 2371 | // the browsers default behavior 2372 | if( triggered ) { 2373 | event.preventDefault(); 2374 | } 2375 | // ESC or O key 2376 | else if ( ( event.keyCode === 27 || event.keyCode === 79 ) && supports3DTransforms ) { 2377 | toggleOverview(); 2378 | 2379 | event.preventDefault(); 2380 | } 2381 | 2382 | // If auto-sliding is enabled we need to cue up 2383 | // another timeout 2384 | cueAutoSlide(); 2385 | 2386 | } 2387 | 2388 | /** 2389 | * Handler for the 'touchstart' event, enables support for 2390 | * swipe and pinch gestures. 2391 | */ 2392 | function onTouchStart( event ) { 2393 | 2394 | touch.startX = event.touches[0].clientX; 2395 | touch.startY = event.touches[0].clientY; 2396 | touch.startCount = event.touches.length; 2397 | 2398 | // If there's two touches we need to memorize the distance 2399 | // between those two points to detect pinching 2400 | if( event.touches.length === 2 && config.overview ) { 2401 | touch.startSpan = distanceBetween( { 2402 | x: event.touches[1].clientX, 2403 | y: event.touches[1].clientY 2404 | }, { 2405 | x: touch.startX, 2406 | y: touch.startY 2407 | } ); 2408 | } 2409 | 2410 | } 2411 | 2412 | /** 2413 | * Handler for the 'touchmove' event. 2414 | */ 2415 | function onTouchMove( event ) { 2416 | 2417 | // Each touch should only trigger one action 2418 | if( !touch.captured ) { 2419 | var currentX = event.touches[0].clientX; 2420 | var currentY = event.touches[0].clientY; 2421 | 2422 | // If the touch started with two points and still has 2423 | // two active touches; test for the pinch gesture 2424 | if( event.touches.length === 2 && touch.startCount === 2 && config.overview ) { 2425 | 2426 | // The current distance in pixels between the two touch points 2427 | var currentSpan = distanceBetween( { 2428 | x: event.touches[1].clientX, 2429 | y: event.touches[1].clientY 2430 | }, { 2431 | x: touch.startX, 2432 | y: touch.startY 2433 | } ); 2434 | 2435 | // If the span is larger than the desire amount we've got 2436 | // ourselves a pinch 2437 | if( Math.abs( touch.startSpan - currentSpan ) > touch.threshold ) { 2438 | touch.captured = true; 2439 | 2440 | if( currentSpan < touch.startSpan ) { 2441 | activateOverview(); 2442 | } 2443 | else { 2444 | deactivateOverview(); 2445 | } 2446 | } 2447 | 2448 | event.preventDefault(); 2449 | 2450 | } 2451 | // There was only one touch point, look for a swipe 2452 | else if( event.touches.length === 1 && touch.startCount !== 2 ) { 2453 | 2454 | var deltaX = currentX - touch.startX, 2455 | deltaY = currentY - touch.startY; 2456 | 2457 | if( deltaX > touch.threshold && Math.abs( deltaX ) > Math.abs( deltaY ) ) { 2458 | touch.captured = true; 2459 | navigateLeft(); 2460 | } 2461 | else if( deltaX < -touch.threshold && Math.abs( deltaX ) > Math.abs( deltaY ) ) { 2462 | touch.captured = true; 2463 | navigateRight(); 2464 | } 2465 | else if( deltaY > touch.threshold ) { 2466 | touch.captured = true; 2467 | navigateUp(); 2468 | } 2469 | else if( deltaY < -touch.threshold ) { 2470 | touch.captured = true; 2471 | navigateDown(); 2472 | } 2473 | 2474 | // If we're embedded, only block touch events if they have 2475 | // triggered an action 2476 | if( config.embedded ) { 2477 | if( touch.captured || isVerticalSlide( currentSlide ) ) { 2478 | event.preventDefault(); 2479 | } 2480 | } 2481 | // Not embedded? Block them all to avoid needless tossing 2482 | // around of the viewport in iOS 2483 | else { 2484 | event.preventDefault(); 2485 | } 2486 | 2487 | } 2488 | } 2489 | // There's a bug with swiping on some Android devices unless 2490 | // the default action is always prevented 2491 | else if( navigator.userAgent.match( /android/gi ) ) { 2492 | event.preventDefault(); 2493 | } 2494 | 2495 | } 2496 | 2497 | /** 2498 | * Handler for the 'touchend' event. 2499 | */ 2500 | function onTouchEnd( event ) { 2501 | 2502 | touch.captured = false; 2503 | 2504 | } 2505 | 2506 | /** 2507 | * Convert pointer down to touch start. 2508 | */ 2509 | function onPointerDown( event ) { 2510 | 2511 | if( event.pointerType === event.MSPOINTER_TYPE_TOUCH ) { 2512 | event.touches = [{ clientX: event.clientX, clientY: event.clientY }]; 2513 | onTouchStart( event ); 2514 | } 2515 | 2516 | } 2517 | 2518 | /** 2519 | * Convert pointer move to touch move. 2520 | */ 2521 | function onPointerMove( event ) { 2522 | 2523 | if( event.pointerType === event.MSPOINTER_TYPE_TOUCH ) { 2524 | event.touches = [{ clientX: event.clientX, clientY: event.clientY }]; 2525 | onTouchMove( event ); 2526 | } 2527 | 2528 | } 2529 | 2530 | /** 2531 | * Convert pointer up to touch end. 2532 | */ 2533 | function onPointerUp( event ) { 2534 | 2535 | if( event.pointerType === event.MSPOINTER_TYPE_TOUCH ) { 2536 | event.touches = [{ clientX: event.clientX, clientY: event.clientY }]; 2537 | onTouchEnd( event ); 2538 | } 2539 | 2540 | } 2541 | 2542 | /** 2543 | * Handles mouse wheel scrolling, throttled to avoid skipping 2544 | * multiple slides. 2545 | */ 2546 | function onDocumentMouseScroll( event ) { 2547 | 2548 | if( Date.now() - lastMouseWheelStep > 600 ) { 2549 | 2550 | lastMouseWheelStep = Date.now(); 2551 | 2552 | var delta = event.detail || -event.wheelDelta; 2553 | if( delta > 0 ) { 2554 | navigateNext(); 2555 | } 2556 | else { 2557 | navigatePrev(); 2558 | } 2559 | 2560 | } 2561 | 2562 | } 2563 | 2564 | /** 2565 | * Clicking on the progress bar results in a navigation to the 2566 | * closest approximate horizontal slide using this equation: 2567 | * 2568 | * ( clickX / presentationWidth ) * numberOfSlides 2569 | */ 2570 | function onProgressClicked( event ) { 2571 | 2572 | event.preventDefault(); 2573 | 2574 | var slidesTotal = toArray( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ).length; 2575 | var slideIndex = Math.floor( ( event.clientX / dom.wrapper.offsetWidth ) * slidesTotal ); 2576 | 2577 | slide( slideIndex ); 2578 | 2579 | } 2580 | 2581 | /** 2582 | * Event handler for navigation control buttons. 2583 | */ 2584 | function onNavigateLeftClicked( event ) { event.preventDefault(); navigateLeft(); } 2585 | function onNavigateRightClicked( event ) { event.preventDefault(); navigateRight(); } 2586 | function onNavigateUpClicked( event ) { event.preventDefault(); navigateUp(); } 2587 | function onNavigateDownClicked( event ) { event.preventDefault(); navigateDown(); } 2588 | function onNavigatePrevClicked( event ) { event.preventDefault(); navigatePrev(); } 2589 | function onNavigateNextClicked( event ) { event.preventDefault(); navigateNext(); } 2590 | 2591 | /** 2592 | * Handler for the window level 'hashchange' event. 2593 | */ 2594 | function onWindowHashChange( event ) { 2595 | 2596 | readURL(); 2597 | 2598 | } 2599 | 2600 | /** 2601 | * Handler for the window level 'resize' event. 2602 | */ 2603 | function onWindowResize( event ) { 2604 | 2605 | layout(); 2606 | 2607 | } 2608 | 2609 | /** 2610 | * Invoked when a slide is and we're in the overview. 2611 | */ 2612 | function onOverviewSlideClicked( event ) { 2613 | 2614 | // TODO There's a bug here where the event listeners are not 2615 | // removed after deactivating the overview. 2616 | if( eventsAreBound && isOverview() ) { 2617 | event.preventDefault(); 2618 | 2619 | var element = event.target; 2620 | 2621 | while( element && !element.nodeName.match( /section/gi ) ) { 2622 | element = element.parentNode; 2623 | } 2624 | 2625 | if( element && !element.classList.contains( 'disabled' ) ) { 2626 | 2627 | deactivateOverview(); 2628 | 2629 | if( element.nodeName.match( /section/gi ) ) { 2630 | var h = parseInt( element.getAttribute( 'data-index-h' ), 10 ), 2631 | v = parseInt( element.getAttribute( 'data-index-v' ), 10 ); 2632 | 2633 | slide( h, v ); 2634 | } 2635 | 2636 | } 2637 | } 2638 | 2639 | } 2640 | 2641 | /** 2642 | * Handles clicks on links that are set to preview in the 2643 | * iframe overlay. 2644 | */ 2645 | function onPreviewLinkClicked( event ) { 2646 | 2647 | var url = event.target.getAttribute( 'href' ); 2648 | if( url ) { 2649 | openPreview( url ); 2650 | event.preventDefault(); 2651 | } 2652 | 2653 | } 2654 | 2655 | 2656 | // --------------------------------------------------------------------// 2657 | // ------------------------------- API --------------------------------// 2658 | // --------------------------------------------------------------------// 2659 | 2660 | 2661 | return { 2662 | initialize: initialize, 2663 | configure: configure, 2664 | sync: sync, 2665 | 2666 | // Navigation methods 2667 | slide: slide, 2668 | left: navigateLeft, 2669 | right: navigateRight, 2670 | up: navigateUp, 2671 | down: navigateDown, 2672 | prev: navigatePrev, 2673 | next: navigateNext, 2674 | prevFragment: previousFragment, 2675 | nextFragment: nextFragment, 2676 | 2677 | // Deprecated aliases 2678 | navigateTo: slide, 2679 | navigateLeft: navigateLeft, 2680 | navigateRight: navigateRight, 2681 | navigateUp: navigateUp, 2682 | navigateDown: navigateDown, 2683 | navigatePrev: navigatePrev, 2684 | navigateNext: navigateNext, 2685 | 2686 | // Forces an update in slide layout 2687 | layout: layout, 2688 | 2689 | // Returns an object with the available routes as booleans (left/right/top/bottom) 2690 | availableRoutes: availableRoutes, 2691 | 2692 | // Returns an object with the available fragments as booleans (prev/next) 2693 | availableFragments: availableFragments, 2694 | 2695 | // Toggles the overview mode on/off 2696 | toggleOverview: toggleOverview, 2697 | 2698 | // Toggles the "black screen" mode on/off 2699 | togglePause: togglePause, 2700 | 2701 | // State checks 2702 | isOverview: isOverview, 2703 | isPaused: isPaused, 2704 | 2705 | // Adds or removes all internal event listeners (such as keyboard) 2706 | addEventListeners: addEventListeners, 2707 | removeEventListeners: removeEventListeners, 2708 | 2709 | // Returns the indices of the current, or specified, slide 2710 | getIndices: getIndices, 2711 | 2712 | // Returns the slide at the specified index, y is optional 2713 | getSlide: function( x, y ) { 2714 | var horizontalSlide = document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR )[ x ]; 2715 | var verticalSlides = horizontalSlide && horizontalSlide.querySelectorAll( 'section' ); 2716 | 2717 | if( typeof y !== 'undefined' ) { 2718 | return verticalSlides ? verticalSlides[ y ] : undefined; 2719 | } 2720 | 2721 | return horizontalSlide; 2722 | }, 2723 | 2724 | // Returns the previous slide element, may be null 2725 | getPreviousSlide: function() { 2726 | return previousSlide; 2727 | }, 2728 | 2729 | // Returns the current slide element 2730 | getCurrentSlide: function() { 2731 | return currentSlide; 2732 | }, 2733 | 2734 | // Returns the current scale of the presentation content 2735 | getScale: function() { 2736 | return scale; 2737 | }, 2738 | 2739 | // Returns the current configuration object 2740 | getConfig: function() { 2741 | return config; 2742 | }, 2743 | 2744 | // Helper method, retrieves query string as a key/value hash 2745 | getQueryHash: function() { 2746 | var query = {}; 2747 | 2748 | location.search.replace( /[A-Z0-9]+?=(\w*)/gi, function(a) { 2749 | query[ a.split( '=' ).shift() ] = a.split( '=' ).pop(); 2750 | } ); 2751 | 2752 | return query; 2753 | }, 2754 | 2755 | // Returns true if we're currently on the first slide 2756 | isFirstSlide: function() { 2757 | return document.querySelector( SLIDES_SELECTOR + '.past' ) == null ? true : false; 2758 | }, 2759 | 2760 | // Returns true if we're currently on the last slide 2761 | isLastSlide: function() { 2762 | if( currentSlide && currentSlide.classList.contains( '.stack' ) ) { 2763 | return currentSlide.querySelector( SLIDES_SELECTOR + '.future' ) == null ? true : false; 2764 | } 2765 | else { 2766 | return document.querySelector( SLIDES_SELECTOR + '.future' ) == null ? true : false; 2767 | } 2768 | }, 2769 | 2770 | // Checks if reveal.js has been loaded and is ready for use 2771 | isReady: function() { 2772 | return loaded; 2773 | }, 2774 | 2775 | // Forward event binding to the reveal DOM element 2776 | addEventListener: function( type, listener, useCapture ) { 2777 | if( 'addEventListener' in window ) { 2778 | ( dom.wrapper || document.querySelector( '.reveal' ) ).addEventListener( type, listener, useCapture ); 2779 | } 2780 | }, 2781 | removeEventListener: function( type, listener, useCapture ) { 2782 | if( 'addEventListener' in window ) { 2783 | ( dom.wrapper || document.querySelector( '.reveal' ) ).removeEventListener( type, listener, useCapture ); 2784 | } 2785 | } 2786 | }; 2787 | 2788 | })(); 2789 | --------------------------------------------------------------------------------