├── .gitignore ├── Gemfile ├── Gemfile.lock ├── Guardfile ├── Makefile ├── README.md ├── Rules ├── config.rb ├── content ├── 404.haml ├── about.haml ├── apple-touch-icon-57x57.png ├── apple-touch-icon-60x60.png ├── apple-touch-icon-72x72.png ├── apple-touch-icon-76x76.png ├── apple-touch-icon-precomposed.png ├── apple-touch-icon.png ├── assets │ └── css │ │ ├── _fonts.scss │ │ ├── _search.scss │ │ ├── _variables.scss │ │ └── styles.scss ├── favicon-16x16.png ├── favicon-196x196.png ├── favicon-32x32.png ├── favicon-96x96.png ├── favicon.ico ├── index.haml ├── search.haml ├── sitemap.haml └── tags │ └── index.haml ├── images ├── .gitkeep ├── audio_ready_icon.png ├── dvd_ready_icon.png ├── folder.png ├── hd_ready_icon.png ├── logos │ ├── blinkenlights │ │ └── folder-blinkenlights.jpg │ ├── broadcast │ │ └── sendezentrum │ │ │ └── sendezentrum.png │ ├── chaosradio │ │ └── video │ │ │ └── chaosradio-logo-transparent-300.png │ ├── conferences │ │ ├── camp1999 │ │ │ └── camprocket.png │ │ ├── camp2003 │ │ │ └── folder-camp-2003.png │ │ ├── camp2007 │ │ │ └── folder-camp-2007.jpg │ │ ├── camp2011 │ │ │ └── folder-camp-2011.png │ │ ├── camp2015 │ │ │ └── camp15-logo.png │ │ ├── chaoscologne │ │ │ └── 1c2 │ │ │ │ └── 1c2_logo.png │ │ ├── eh2014 │ │ │ └── logo-navbar.png │ │ ├── eh2015 │ │ │ └── eh15v4.png │ │ ├── fiffkon │ │ │ └── 2014 │ │ │ │ └── fiffkon14-logo.png │ │ ├── froscon │ │ │ ├── 2010 │ │ │ │ └── folder-froscon-2010.png │ │ │ ├── 2011 │ │ │ │ └── folder-froscon-2010.png │ │ │ ├── 2012 │ │ │ │ └── froscon.png │ │ │ ├── 2013 │ │ │ │ └── folder-froscon-2010.png │ │ │ ├── 2014 │ │ │ │ └── folder-froscon-2010.png │ │ │ └── 2015 │ │ │ │ └── froscon2015.png │ │ ├── har2009 │ │ │ └── folder-har-2009.png │ │ ├── mrmcd │ │ │ ├── mrmcd0x8 │ │ │ │ └── folder-mrmcd-2009.jpg │ │ │ ├── mrmcd101b │ │ │ │ └── folder-mrmcd-2006.png │ │ │ ├── mrmcd111b │ │ │ │ └── folder-mrmcd-2008.jpg │ │ │ ├── mrmcd13 │ │ │ │ └── mrmcd2013-logo.png │ │ │ ├── mrmcd14 │ │ │ │ └── mrmcd_14.png │ │ │ └── mrmcd15 │ │ │ │ ├── logo-export_mrmcd15.svg │ │ │ │ └── mrmcd_15.png │ │ ├── sigint09 │ │ │ └── folder-sigint-2009.png │ │ ├── sigint10 │ │ │ └── folder-sigint-2010.png │ │ ├── sigint12 │ │ │ └── folder-sigint-2012.jpg │ │ └── sigint13 │ │ │ └── folder-sigint-2013.png │ ├── congress │ │ ├── 2000 │ │ │ └── realmedia │ │ │ │ └── folder-17c3.png │ │ ├── 2001 │ │ │ └── folder-18c3.png │ │ ├── 2002 │ │ │ └── folder-19c3.png │ │ ├── 2003 │ │ │ └── folder-20c3.png │ │ ├── 2004 │ │ │ └── folder-21c3.png │ │ ├── 2005 │ │ │ └── folder-22c3.gif │ │ ├── 2006 │ │ │ └── folder-23c3.png │ │ ├── 2007 │ │ │ └── folder-24c3.jpg │ │ ├── 2008 │ │ │ └── folder-25c3.png │ │ ├── 2009 │ │ │ └── folder-26c3.jpg │ │ ├── 2010 │ │ │ └── folder-27c3.png │ │ ├── 2011 │ │ │ └── folder-28c3.png │ │ ├── 2012 │ │ │ └── folder-29c3.png │ │ ├── 2013 │ │ │ └── folder-30c3.png │ │ └── 2014 │ │ │ └── folder-2014.png │ ├── events │ │ ├── Panoptische_Prinzip │ │ │ └── logo.jpg │ │ ├── cryptocon │ │ │ └── 2015 │ │ │ │ └── cc15_logo_transparent.png │ │ ├── datenspuren │ │ │ ├── 2014 │ │ │ │ └── datenspuren2014-logo.png │ │ │ └── 2015 │ │ │ │ ├── ds15-logo-print.svg │ │ │ │ └── ds2015.png │ │ ├── gpn │ │ │ ├── gpn14 │ │ │ │ └── 80px-Gpn14_logo_navi.jpg.png │ │ │ └── gpn15 │ │ │ │ └── gpn15_logo.png │ │ ├── gpn10 │ │ │ └── 95px-GPN10-TShirt.png │ │ ├── hackover │ │ │ ├── 2013 │ │ │ │ └── hackover-2013.png │ │ │ ├── 2014 │ │ │ │ └── hackover-2014.png │ │ │ └── hackover-logo.png │ │ ├── netzpolitik │ │ │ ├── 10np │ │ │ │ └── 10np.png │ │ │ └── 11np │ │ │ │ └── 11np.png │ │ ├── vcfb │ │ │ └── 2014 │ │ │ │ ├── vcfb-logo-quer.png │ │ │ │ └── vcfb2015-logo.png │ │ ├── wauland │ │ │ └── whs_logo.png │ │ └── wie_werden_kriege_gemacht-koeln_2015 │ │ │ └── unknown.png │ ├── regional │ │ ├── berlin │ │ │ └── datengarten │ │ │ │ └── datengarten.png │ │ ├── c4 │ │ │ └── openchaos │ │ │ │ └── c4-logo.jpg │ │ └── cccac │ │ │ └── vortraege │ │ │ ├── cccac_logo_bw_kleiner.png │ │ │ └── logo.png │ └── unknown.png ├── miro-banner.png ├── promoted_bg.jpg ├── rss_logo.png ├── spinner.gif ├── tv-rausch.gif └── tv.png ├── layouts ├── browse-download-page.haml ├── browse-index.haml ├── browse-oembed-page.haml ├── browse-show-folder.haml ├── browse-show-page.haml ├── browse-show-tags.haml ├── default.haml └── partials │ ├── download.haml │ ├── embedshare.haml │ ├── folder-feeds.haml │ ├── footer.haml │ ├── header.haml │ ├── navbar.haml │ ├── promoted.haml │ ├── search.haml │ ├── videoplayer.haml │ └── videoplayer_js.haml ├── lib ├── data_source.rb ├── data_source │ ├── feed_builder.rb │ ├── item_builder.rb │ ├── location_pages.rb │ └── tag_pages.rb ├── default.rb ├── feeds.rb ├── feeds │ ├── broadcatching.xml.haml │ ├── broadcatching_generator.rb │ ├── helper.rb │ ├── newsfeed_generator.rb │ ├── podcast_generator.rb │ └── rdf_generator.rb ├── helpers.rb ├── helpers │ ├── application_helper.rb │ ├── logo_filter_helper.rb │ └── tagging_helper.rb ├── helpers_.rb ├── magnet_link_provider.rb ├── mime_type.rb └── models │ ├── conference.rb │ ├── event.rb │ ├── folder.rb │ ├── news.rb │ └── recording.rb ├── nanoc.yaml.example ├── settings.json ├── static ├── css │ ├── bigplay.png │ ├── bigplay.svg │ ├── controls.png │ ├── controls.svg │ ├── font-awesome.css │ ├── font-awesome.min.css │ ├── loading.gif │ └── mediaelementplayer.min.css ├── flashmediaelement.swf ├── fonts │ ├── FontAwesome.otf │ ├── bootstrap │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ └── glyphicons-halflings-regular.woff │ ├── estre.eot │ ├── estre.otf │ ├── estre.svg │ ├── estre.ttf │ ├── estre.woff │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ └── fontawesome-webfont.woff └── js │ ├── bootstrap.js │ ├── bootstrap.min.js │ ├── handlebars.min-latest.js │ ├── jquery.min.js │ ├── mediaelement-and-player.min.js │ ├── purl.min.js │ └── search.js └── testdata.sql.gz /.gitignore: -------------------------------------------------------------------------------- 1 | tmp/ 2 | vendor/ 3 | output/ 4 | *sqlite3 5 | .ruby-version 6 | nanoc.yaml 7 | *sql 8 | *.log 9 | .idea 10 | .sass-cache 11 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'nanoc' 4 | gem 'haml' 5 | gem 'compass' 6 | gem 'bootstrap-sass' 7 | 8 | gem 'activerecord', '< 4.0' 9 | gem 'activesupport', '< 4.0' 10 | gem 'nokogiri' 11 | gem 'guard' 12 | gem 'guard-nanoc' 13 | gem 'adsf' 14 | 15 | platforms :jruby do 16 | gem 'activerecord-jdbcsqlite3-adapter' 17 | gem 'kramdown' 18 | end 19 | 20 | platforms :ruby do 21 | gem 'sqlite3' 22 | gem 'redcarpet' 23 | end 24 | 25 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | activemodel (3.2.19) 5 | activesupport (= 3.2.19) 6 | builder (~> 3.0.0) 7 | activerecord (3.2.19) 8 | activemodel (= 3.2.19) 9 | activesupport (= 3.2.19) 10 | arel (~> 3.0.2) 11 | tzinfo (~> 0.3.29) 12 | activesupport (3.2.19) 13 | i18n (~> 0.6, >= 0.6.4) 14 | multi_json (~> 1.0) 15 | adsf (1.2.0) 16 | rack (>= 1.0.0) 17 | arel (3.0.3) 18 | bootstrap-sass (3.2.0.2) 19 | sass (~> 3.2) 20 | builder (3.0.4) 21 | celluloid (0.16.0) 22 | timers (~> 4.0.0) 23 | chunky_png (1.3.1) 24 | coderay (1.1.0) 25 | colored (1.2) 26 | compass (1.0.1) 27 | chunky_png (~> 1.2) 28 | compass-core (~> 1.0.1) 29 | compass-import-once (~> 1.0.5) 30 | rb-fsevent (>= 0.9.3) 31 | rb-inotify (>= 0.9) 32 | sass (>= 3.3.13, < 3.5) 33 | compass-core (1.0.1) 34 | multi_json (~> 1.0) 35 | sass (>= 3.3.0, < 3.5) 36 | compass-import-once (1.0.5) 37 | sass (>= 3.2, < 3.5) 38 | cri (2.6.1) 39 | colored (~> 1.2) 40 | ffi (1.9.3) 41 | formatador (0.2.5) 42 | guard (2.6.1) 43 | formatador (>= 0.2.4) 44 | listen (~> 2.7) 45 | lumberjack (~> 1.0) 46 | pry (>= 0.9.12) 47 | thor (>= 0.18.1) 48 | guard-nanoc (1.0.2) 49 | guard (>= 1.8.0) 50 | nanoc (>= 3.6.3) 51 | haml (4.0.5) 52 | tilt 53 | hitimes (1.2.2) 54 | i18n (0.6.11) 55 | listen (2.7.9) 56 | celluloid (>= 0.15.2) 57 | rb-fsevent (>= 0.9.3) 58 | rb-inotify (>= 0.9) 59 | lumberjack (1.0.9) 60 | method_source (0.8.2) 61 | mini_portile (0.6.0) 62 | multi_json (1.10.1) 63 | nanoc (3.7.3) 64 | cri (~> 2.3) 65 | nokogiri (1.6.3.1) 66 | mini_portile (= 0.6.0) 67 | pry (0.10.1) 68 | coderay (~> 1.1.0) 69 | method_source (~> 0.8.1) 70 | slop (~> 3.4) 71 | rack (1.5.2) 72 | rb-fsevent (0.9.4) 73 | rb-inotify (0.9.5) 74 | ffi (>= 0.5.0) 75 | redcarpet (3.1.2) 76 | sass (3.4.5) 77 | slop (3.6.0) 78 | sqlite3 (1.3.9) 79 | thor (0.19.1) 80 | tilt (2.0.1) 81 | timers (4.0.1) 82 | hitimes 83 | tzinfo (0.3.41) 84 | 85 | PLATFORMS 86 | ruby 87 | 88 | DEPENDENCIES 89 | activerecord (< 4.0) 90 | activerecord-jdbcsqlite3-adapter 91 | activesupport (< 4.0) 92 | adsf 93 | bootstrap-sass 94 | compass 95 | guard 96 | guard-nanoc 97 | haml 98 | kramdown 99 | nanoc 100 | nokogiri 101 | redcarpet 102 | sqlite3 103 | -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | guard 'nanoc' do 2 | watch 'nanoc.yaml' 3 | watch 'Rules' 4 | watch %r{\A(content|layouts|lib|static)/.*\z} 5 | ignore %r{(?:4913)$} 6 | notification :off 7 | end 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: 3 | git pull 4 | FAST_NANOC=1 nanoc co 5 | 6 | full: 7 | rm -fr tmp/cache 8 | rm /srv/www/media.ccc.de/output/index.html 9 | git pull 10 | nanoc co 11 | 12 | clean: 13 | rm -fr /srv/www/media.ccc.de/output/* 14 | rm -fr tmp/cache 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # media-frontend 2 | 3 | media.ccc.de webfrontend 4 | 5 | ## Install 6 | 7 | ### Ruby Version 8 | 9 | ruby 2.1 10 | 11 | ### Instructions 12 | 13 | The logo filter requires graphicmagic 14 | 15 | aptitude install graphicsmagick 16 | 17 | Install required ruby packages with bundler: 18 | 19 | gem install bundler 20 | bundle install 21 | 22 | 23 | Create config file 24 | 25 | cp nanoc.yaml.example nanoc.yaml 26 | 27 | 28 | ### Database Creation 29 | 30 | Import a database dump 31 | 32 | zcat testdata.sql.gz | sqlite3 development.sqlite3 33 | 34 | ## Nanoc 35 | 36 | [Nanoc](http://nanoc.ws) is a static site generator 37 | 38 | ### Compile static pages 39 | 40 | nanoc compile 41 | 42 | ### Start a webserver to view pages in a browser 43 | 44 | nanoc view 45 | 46 | ### Watch output directory for changes 47 | 48 | guard 49 | 50 | # JRuby 51 | 52 | export JRUBY_OPTS="--2.0 -J-Xmn512m -J-Xms2048m -J-Xmx2048m -J-server" 53 | 54 | 55 | ~/.jrubyrc 56 | 57 | compat.version=2.0 58 | -------------------------------------------------------------------------------- /Rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | unless defined? LOADED_DEFAULT_CONFIG 4 | LOADED_DEFAULT_CONFIG = true 5 | require 'compass' 6 | Compass.add_project_configuration 'config.rb' 7 | end 8 | 9 | # A few helpful tips about the Rules file: 10 | # 11 | # * The string given to #compile and #route are matching patterns for 12 | # identifiers--not for paths. Therefore, you can’t match on extension. 13 | # 14 | # * The order of rules is important: for each item, only the first matching 15 | # rule is applied. 16 | # 17 | # * Item identifiers start and end with a slash (e.g. “/about/” for the file 18 | # “content/about.html”). To select all children, grandchildren, … of an 19 | # item, use the pattern “/about/*/”; “/about/*” will also select the parent, 20 | # because “*” matches zero or more characters. 21 | 22 | compile '/browse/*' do 23 | if item[:layout].present? 24 | layout item[:layout] 25 | else 26 | nil 27 | end 28 | end 29 | 30 | compile 'sitemap' do 31 | filter :haml 32 | end 33 | 34 | route 'sitemap' do 35 | item.identifier.chop + '.xml' 36 | end 37 | 38 | route '/images/logos/*' do 39 | item.identifier.chop.chomp(item[:extension]) + 'png' 40 | end 41 | 42 | compile '/images/logos/*' do 43 | if item.binary? 44 | filter :logo 45 | end 46 | end 47 | 48 | # / 49 | compile '*' do 50 | unless item.binary? 51 | case item[:extension] 52 | when /scss/ 53 | filter :sass, Compass.sass_engine_options 54 | when 'haml' 55 | filter :haml, { :ugly => true, format: :html5 } 56 | layout 'default' 57 | else 58 | nil 59 | end 60 | end 61 | end 62 | 63 | # 64 | # Routes 65 | # 66 | 67 | # Assets 68 | route '/assets/**/_*' do 69 | # _mixed.scss 70 | nil 71 | end 72 | 73 | route '/assets/css/*/' do 74 | if item[:extension] == 'scss' 75 | item.identifier.chop + '.css' 76 | else 77 | item.identifier.chop 78 | end 79 | end 80 | 81 | passthrough '/assets/*' 82 | passthrough '/images/*' 83 | 84 | # AR datasource 85 | route '/browse/*' do 86 | if %w{browse-show-page browse-download-page browse-oembed-page browse-show-tags}.include? item[:layout] 87 | item.identifier.chop + '.' + 'html' 88 | elsif %w{rss xml}.include? item[:extension] 89 | # podcast.xml 90 | item.identifier.chop + '.' + item[:extension] 91 | else 92 | item.identifier + 'index.html' 93 | end 94 | end 95 | 96 | # Content 97 | route '*' do 98 | if item.binary? 99 | item.identifier.chop + '.' + item[:extension] 100 | elsif %w{/about/ /404/}.include? item.identifier 101 | # HAML pages in root except index.html 102 | item.identifier.chop + '.html' 103 | elsif %w{xml rdf atom}.include? item[:extension] 104 | # atom, xml or rdf feeds 105 | item.identifier.chop + '.' + item[:extension] 106 | else 107 | item.identifier + 'index.html' 108 | end 109 | end 110 | 111 | layout '*', :haml, {:ugly => true, format: :html5} 112 | -------------------------------------------------------------------------------- /config.rb: -------------------------------------------------------------------------------- 1 | # Require any additional compass plugins here. 2 | 3 | require 'bootstrap-sass' 4 | 5 | # Set this to the root of your project when deployed: 6 | http_path = "/" 7 | project_path = "." 8 | css_dir = "output/assets/css" 9 | sass_dir = "content/assets/css" 10 | images_dir = "output/images" 11 | 12 | # when using SCSS: 13 | sass_options = { 14 | :syntax => :scss 15 | } 16 | 17 | # You can select your preferred output style here (can be overridden via the command line): 18 | output_style = :compressed 19 | 20 | # To enable relative paths to assets via compass helper functions. Uncomment: 21 | relative_assets = true 22 | 23 | # To disable debugging comments that display the original location of your selectors. Uncomment: 24 | # line_comments = false 25 | -------------------------------------------------------------------------------- /content/404.haml: -------------------------------------------------------------------------------- 1 | --- 2 | title: 404 - Page not found 3 | --- 4 | 5 | = render 'partials/search', :title => '404 - Page not found' 6 | -------------------------------------------------------------------------------- /content/about.haml: -------------------------------------------------------------------------------- 1 | --- 2 | title: about media.ccc.de 3 | --- 4 | %script(src="/assets/js/handlebars.min-latest.js") 5 | 6 | %div.container 7 | 8 | %h1 About media.ccc.de 9 | 10 | %p 11 | This site offers a wide variety of video and audio material distributed by the Chaos Computer Club provided in native formats (usually MPEG and/or Vorbis families) for online viewing. Older, archived recordings might require propritary players. The media files on this site can also be downloaded for offline consumption. 12 | 13 | %p 14 | If you are anyhow related to the Chaos Computer Club and would like us to convert and publish your video material on this website, feel free to contact us. 15 | 16 | Whilst we hope you find the provided video material interesting and informative we can give no assurances or warranty regarding its quality. The opinions expressed in the video material do not necessarily state or reflect those of the Chaos Computer Club. 17 | 18 | %b Contact: 19 | %a{href: 'mailto:media@koeln.ccc.de'} media@koeln.ccc.de 20 | 21 | %h2 Licenses 22 | 23 | The license displayed in the actual video file applies. If there is no license encoded in the video file we suggest you ask the 24 | organizers of the recordings event. 25 | 26 | %h2 Privacy Policy 27 | 28 | %p 29 | While browsing through the website normally we do not save any information wich could be used to identify a person. The log files are anonymized before they are saved and we do not have a de-anonymized version of the them. The anonymized log-files may be used to plot usage statistics. 30 | 31 | The following data is collected and saved for an undefined duration: 32 | 33 | %ul 34 | %li Date and time, 35 | %li the visited pages, 36 | %li status codes the webserver answered with, 37 | %li your browser and operating system, 38 | %li and the referring URL 39 | 40 | %p 41 | If a connection to port 80 fails, an error log will be created. It contains the IP address of the host causing the error and the website containing the faulty hyperlink. This error log will only be used to fix the error and will not be used in any other way. 42 | 43 | %p 44 | Video and audio files are often provided by mirrors and are not covered by this policy. 45 | 46 | %h2 cdn.media.ccc.de 47 | 48 | %p 49 | :markdown 50 | The website is basically a frontend for all files stored on [cdn.media.ccc.de](//cdn.media.ccc.de). 51 | If you'd rather use a torrent you can append '.torrent' to any large file, this will provide a torrent with 52 | http seeds appropiate for your location. 53 | 54 | %h3 Current Mirrors 55 | 56 | #status 57 | 58 | %h2 Apps 59 | 60 | %ul 61 | %li 62 | %a{href: 'http://addons.xbmc.org/show/plugin.video.media-ccc-de/'} Kodi/XBMC plugin 63 | %li 64 | %a{href: 'https://github.com/cccc/MediaCCC.bundle'} Plex plugin (not released yet) 65 | 66 | %h2 Bug reports and feature requests 67 | %p 68 | Please use the 69 | %a{href: 'https://github.com/voc/media-frontend/issues'} issue tracker 70 | on github to let us know about errors or ideas to improve 71 | %a{href: '//media.ccc.de'} media.ccc.de 72 | \. The latest source code version of this platform is also hosted on 73 | %a{href: 'https://github.com/voc/media-frontend'} github 74 | and ready to expect your pull-requests. 75 | 76 | %h2 Imprint 77 | 78 | %p 79 | :markdown 80 | Please see [ccc.de](//ccc.de/de/imprint). 81 | 82 | %script#status-template{type: 'text/x-handlebars-template'} 83 |
Name | 86 |Location | 87 |Last Scan | 88 |Files | 89 |
---|---|---|---|
{{this.identifier}} | 97 |{{this.region}}/{{this.country}} | 98 |{{this.last_scan}} | 99 |{{this.nfiles}} | 100 |
`, ``, and ``.
52 | // $font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace
53 | $font-family-base: $font-family-sans-serif;
54 | //$font-family: "Droid Sans",Arial,"Trebuchet MS",sans-serif;
55 |
56 | $font-size-base: 14px;
57 | $font-size-large: ceil(($font-size-base * 1.7)); // ~24px
58 | // $font-size-small: ceil(($font-size-base * 0.85)) // ~12px
59 |
60 | $font-size-h1: floor(($font-size-base * 3)); // ~42px
61 | $font-size-h2: floor(($font-size-base * 2.15)); // ~30px
62 | // $font-size-h3: ceil(($font-size-base * 1.7)) // ~24px
63 | // $font-size-h4: ceil(($font-size-base * 1.25)) // ~18px
64 | // $font-size-h5: $font-size-base
65 | // $font-size-h6: ceil(($font-size-base * 0.85)) // ~12px
66 |
67 | //* Unit-less `line-height` for use in components like buttons.
68 | $line-height-base: 1.2;
69 | //* Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.
70 | $line-height-computed: floor(($font-size-base * $line-height-base));
71 |
72 | //* By default, this inherits from the ``.
73 | // $headings-font-family: inherit
74 | // $headings-font-weight: 500
75 | // $headings-line-height: 1.1
76 | $headings-color: $gray;
77 |
78 | //== Iconography
79 | //
80 | //## Specify custom location and filename of the included Glyphicons icon font. Useful for those including Bootstrap via Bower.
81 |
82 | //* Load fonts from this directory.
83 | $icon-font-path: "../fonts/";
84 | //* File name for all font files.
85 | $icon-font-name: "fontawesome-webfont";
86 | //* Element ID within SVG icon file.
87 | $icon-font-svg-id: "fontawesomeregular";
88 |
89 | //== Components
90 | //
91 | //## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).
92 |
93 | $padding-base-vertical: 4px;
94 | // $padding-base-horizontal: 12px
95 |
96 | // $padding-large-vertical: 10px
97 | // $padding-large-horizontal: 16px
98 |
99 | // $padding-small-vertical: 5px
100 | // $padding-small-horizontal: 10px
101 |
102 | // $padding-xs-vertical: 1px
103 | // $padding-xs-horizontal: 5px
104 |
105 | // $line-height-large: 1.33
106 | // $line-height-small: 1.5
107 |
108 | $border-radius-base: 0;
109 | $border-radius-large: 0;
110 | $border-radius-small: 0;
111 |
112 | //* Global color for active items (e.g., navs or dropdowns).
113 | // $component-active-color: #fff
114 | //* Global background color for active items (e.g., navs or dropdowns).
115 | // $component-active-bg: $brand-primary
116 |
117 | //* Width of the `border` for generating carets that indicator dropdowns.
118 | // $caret-width-base: 4px
119 | //* Carets increase slightly in size for larger components.
120 | // $caret-width-large: 5px
121 |
122 | //== Tables
123 | //
124 | //## Customizes the `.table` component with basic values, each used across all table variations.
125 |
126 | //* Padding for ``s and ` `s.
127 | // $table-cell-padding: 8px
128 | //* Padding for cells in `.table-condensed`.
129 | // $table-condensed-cell-padding: 5px
130 |
131 | //* Default background color used for all tables.
132 | // $table-bg: transparent
133 | //* Background color used for `.table-striped`.
134 | // $table-bg-accent: #f9f9f9
135 | //* Background color used for `.table-hover`.
136 | // $table-bg-hover: #f5f5f5
137 | // $table-bg-active: $table-bg-hover
138 |
139 | //* Border color for table and cell borders.
140 | // $table-border-color: #ddd
141 |
142 | //== Buttons
143 | //
144 | //## For each of Bootstrap's buttons, define text, background and border color.
145 |
146 | // $btn-font-weight: normal
147 |
148 | $btn-default-color: $gray-dark;
149 | $btn-default-bg: #fff;
150 | $btn-default-border: #fff;
151 |
152 | $btn-primary-color: #fff;
153 | $btn-primary-bg: $gray-dark;
154 | $btn-primary-border: $gray-dark;
155 |
156 | // $btn-success-color: #fff
157 | // $btn-success-bg: $brand-success
158 | // $btn-success-border: darken($btn-success-bg, 5%)
159 |
160 | // $btn-info-color: #fff
161 | // $btn-info-bg: $brand-info
162 | // $btn-info-border: darken($btn-info-bg, 5%)
163 |
164 | // $btn-warning-color: #fff
165 | // $btn-warning-bg: $brand-warning
166 | // $btn-warning-border: darken($btn-warning-bg, 5%)
167 |
168 | // $btn-danger-color: #fff
169 | // $btn-danger-bg: $brand-danger
170 | // $btn-danger-border: darken($btn-danger-bg, 5%)
171 |
172 | // $btn-link-disabled-color: $gray-light
173 |
174 | //== Forms
175 | //
176 | //##
177 |
178 | //* `` background color
179 | // $input-bg: #fff
180 | //* `` background color
181 | // $input-bg-disabled: $gray-lighter
182 |
183 | //* Text color for ``s
184 | // $input-color: $gray
185 | //* `` border color
186 | $input-border: transparent;
187 | //* `` border radius
188 | $input-border-radius: $border-radius-base;
189 | //* Border color for inputs on focus
190 | $input-border-focus: $brand-primary;
191 |
192 | //* Placeholder text color
193 | $input-color-placeholder: $gray-light;
194 |
195 | //* Default `.form-control` height
196 | $input-height-base: ($line-height-computed + ($padding-base-vertical * 2) + 2);
197 | //* Large `.form-control` height
198 | // $input-height-large: (ceil($font-size-large * $line-height-large) + ($padding-large-vertical * 2) + 2)
199 | //* Small `.form-control` height
200 | // $input-height-small: (floor($font-size-small * $line-height-small) + ($padding-small-vertical * 2) + 2)
201 |
202 | // $legend-color: $gray-dark
203 | // $legend-border-color: #e5e5e5
204 |
205 | //* Background color for textual input addons
206 | // $input-group-addon-bg: $gray-lighter
207 | //* Border color for textual input addons
208 | // $input-group-addon-border-color: $input-border
209 |
210 | //== Dropdowns
211 | //
212 | //## Dropdown menu container and contents.
213 |
214 | //* Background for the dropdown menu.
215 | // $dropdown-bg: #fff
216 | //* Dropdown menu `border-color`.
217 | // $dropdown-border: rgba(0,0,0,.15)
218 | //* Dropdown menu `border-color` **for IE8**.
219 | // $dropdown-fallback-border: #ccc
220 | //* Divider color for between dropdown items.
221 | // $dropdown-divider-bg: #e5e5e5
222 |
223 | //* Dropdown link text color.
224 | // $dropdown-link-color: $gray-dark
225 | //* Hover color for dropdown links.
226 | // $dropdown-link-hover-color: darken($gray-dark, 5%)
227 | //* Hover background for dropdown links.
228 | // $dropdown-link-hover-bg: #f5f5f5
229 |
230 | //* Active dropdown menu item text color.
231 | // $dropdown-link-active-color: $component-active-color
232 | //* Active dropdown menu item background color.
233 | // $dropdown-link-active-bg: $component-active-bg
234 |
235 | //* Disabled dropdown menu item background color.
236 | // $dropdown-link-disabled-color: $gray-light
237 |
238 | //* Text color for headers within dropdown menus.
239 | // $dropdown-header-color: $gray-light
240 |
241 | // Note: Deprecated $dropdown-caret-color as of v3.1.0
242 | // $dropdown-caret-color: #000
243 |
244 | //-- Z-index master list
245 | //
246 | // Warning: Avoid customizing these values. They're used for a bird's eye view
247 | // of components dependent on the z-axis and are designed to all work together.
248 | //
249 | // Note: These variables are not generated into the Customizer.
250 |
251 | // $zindex-navbar: 1000
252 | // $zindex-dropdown: 1000
253 | // $zindex-popover: 1060
254 | // $zindex-tooltip: 1070
255 | // $zindex-navbar-fixed: 1030
256 | // $zindex-modal-background: 1040
257 | // $zindex-modal: 1050
258 |
259 | //== Media queries breakpoints
260 | //
261 | //## Define the breakpoints at which your layout will change, adapting to different screen sizes.
262 |
263 | // Extra small screen / phone
264 | // Note: Deprecated $screen-xs and $screen-phone as of v3.0.1
265 | // Note: Deprecated $screen-xs-min as of v3.2.0
266 | // $screen-xs: 480px
267 | // $screen-xs-min: $screen-xs
268 | // $screen-phone: $screen-xs-min
269 |
270 | // Small screen / tablet
271 | // Note: Deprecated $screen-sm and $screen-tablet as of v3.0.1
272 | $screen-sm: 450px;
273 | $screen-sm-min: $screen-sm;
274 | $screen-tablet: $screen-sm-min;
275 |
276 | // Medium screen / desktop
277 | // Note: Deprecated $screen-md and $screen-desktop as of v3.0.1
278 | $screen-md: 960px;
279 | $screen-md-min: $screen-md;
280 | $screen-desktop: $screen-md-min;
281 |
282 | // Large screen / wide desktop
283 | // Note: Deprecated $screen-lg and $screen-lg-desktop as of v3.0.1
284 | $screen-lg: 1200px;
285 | $screen-lg-min: $screen-lg;
286 | $screen-lg-desktop: $screen-lg-min;
287 |
288 | // So media queries don't overlap when required, provide a maximum
289 | $screen-xs-max: ($screen-sm-min - 1);
290 | $screen-sm-max: ($screen-md-min - 1);
291 | $screen-md-max: ($screen-lg-min - 1);
292 |
293 | //== Grid system
294 | //
295 | //## Define your custom responsive grid.
296 |
297 | //* Number of columns in the grid.
298 | // $grid-columns: 12
299 | //* Padding between columns. Gets divided in half for the left and right.
300 | // $grid-gutter-width: 30px
301 | // Navbar collapse
302 | //* Point at which the navbar becomes uncollapsed.
303 | $grid-float-breakpoint: 0; //$screen-sm-min;
304 | //* Point at which the navbar begins collapsing.
305 | $grid-float-breakpoint-max: 0; //($grid-float-breakpoint - 1);
306 |
307 | //== Container sizes
308 | //
309 | //## Define the maximum width of `.container` for different screen sizes.
310 |
311 | // Small screen / tablet
312 | $container-tablet: 700px;
313 | //* For `$screen-sm-min` and up.
314 | $container-sm: $container-tablet;
315 |
316 | // Medium screen / desktop
317 | $container-desktop: 700px;
318 | //* For `$screen-md-min` and up.
319 | $container-md: $container-desktop;
320 |
321 | // Large screen / wide desktop
322 | $container-large-desktop: 700px;
323 | //* For `$screen-lg-min` and up.
324 | $container-lg: $container-large-desktop;
325 |
326 | //== Navbar
327 | //
328 | //##
329 |
330 | // Basics of a navbar
331 | $navbar-height: 48px;
332 | $navbar-margin-bottom: 0;
333 | $navbar-border-radius: $border-radius-base;
334 | // $navbar-padding-horizontal: floor(($grid-gutter-width / 2))
335 | $navbar-padding-vertical: (($navbar-height - $line-height-computed) / 2);
336 | // $navbar-collapse-max-height: 340px
337 |
338 | // $navbar-default-color: #777
339 | $navbar-default-bg: $gray-dark;
340 | $navbar-default-border: transparent;
341 |
342 | // Navbar links
343 | $navbar-default-link-color: $gray-dark;
344 | $navbar-default-link-hover-color: $brand-primary;
345 | $navbar-default-link-hover-bg: #fff;
346 | // $navbar-default-link-active-color: #555
347 | // $navbar-default-link-active-bg: darken($navbar-default-bg, 6.5%)
348 | // $navbar-default-link-disabled-color: #ccc
349 | // $navbar-default-link-disabled-bg: transparent
350 |
351 | // Navbar brand label
352 | $navbar-default-brand-color: #fff;
353 | $navbar-default-brand-hover-color: $gray-lighter;
354 | // $navbar-default-brand-hover-bg: transparent
355 |
356 | // Navbar toggle
357 | // $navbar-default-toggle-hover-bg: #ddd
358 | // $navbar-default-toggle-icon-bar-bg: #888
359 | // $navbar-default-toggle-border-color: #ddd
360 |
361 | // Inverted navbar
362 | // Reset inverted navbar basics
363 | // $navbar-inverse-color: $gray-light
364 | // $navbar-inverse-bg: #222
365 | // $navbar-inverse-border: darken($navbar-inverse-bg, 10%)
366 |
367 | // Inverted navbar links
368 | // $navbar-inverse-link-color: $gray-light
369 | // $navbar-inverse-link-hover-color: #fff
370 | // $navbar-inverse-link-hover-bg: transparent
371 | // $navbar-inverse-link-active-color: $navbar-inverse-link-hover-color
372 | // $navbar-inverse-link-active-bg: darken($navbar-inverse-bg, 10%)
373 | // $navbar-inverse-link-disabled-color: #444
374 | // $navbar-inverse-link-disabled-bg: transparent
375 |
376 | // Inverted navbar brand label
377 | // $navbar-inverse-brand-color: $navbar-inverse-link-color
378 | // $navbar-inverse-brand-hover-color: #fff
379 | // $navbar-inverse-brand-hover-bg: transparent
380 |
381 | // Inverted navbar toggle
382 | // $navbar-inverse-toggle-hover-bg: #333
383 | // $navbar-inverse-toggle-icon-bar-bg: #fff
384 | // $navbar-inverse-toggle-border-color: #333
385 |
386 | //== Navs
387 | //
388 | //##
389 |
390 | //=== Shared nav styles
391 | $nav-link-padding: 0px 10px;
392 | $nav-link-hover-bg: #fff;
393 |
394 | // $nav-disabled-link-color: $gray-light
395 | // $nav-disabled-link-hover-color: $gray-light
396 |
397 | // $nav-open-link-hover-color: #fff
398 |
399 | //== Tabs
400 | // $nav-tabs-border-color: #ddd
401 |
402 | // $nav-tabs-link-hover-border-color: $gray-lighter
403 |
404 | // $nav-tabs-active-link-hover-bg: $body-bg
405 | // $nav-tabs-active-link-hover-color: $gray
406 | // $nav-tabs-active-link-hover-border-color: #ddd
407 |
408 | // $nav-tabs-justified-link-border-color: #ddd
409 | // $nav-tabs-justified-active-link-border-color: $body-bg
410 |
411 | //== Pills
412 | $nav-pills-border-radius: $border-radius-base;
413 | // $nav-pills-active-link-hover-bg: $component-active-bg
414 | // $nav-pills-active-link-hover-color: $component-active-color
415 |
416 | //== Pagination
417 | //
418 | //##
419 |
420 | // $pagination-color: $link-color
421 | // $pagination-bg: #fff
422 | // $pagination-border: #ddd
423 |
424 | // $pagination-hover-color: $link-hover-color
425 | // $pagination-hover-bg: $gray-lighter
426 | // $pagination-hover-border: #ddd
427 |
428 | // $pagination-active-color: #fff
429 | // $pagination-active-bg: $brand-primary
430 | // $pagination-active-border: $brand-primary
431 |
432 | // $pagination-disabled-color: $gray-light
433 | // $pagination-disabled-bg: #fff
434 | // $pagination-disabled-border: #ddd
435 |
436 | //== Pager
437 | //
438 | //##
439 |
440 | // $pager-bg: $pagination-bg
441 | // $pager-border: $pagination-border
442 | // $pager-border-radius: 15px
443 |
444 | // $pager-hover-bg: $pagination-hover-bg
445 |
446 | // $pager-active-bg: $pagination-active-bg
447 | // $pager-active-color: $pagination-active-color
448 |
449 | // $pager-disabled-color: $pagination-disabled-color
450 |
451 | //== Jumbotron
452 | //
453 | //##
454 |
455 | // $jumbotron-padding: 30px
456 | // $jumbotron-color: inherit
457 | // $jumbotron-bg: $gray-lighter
458 | // $jumbotron-heading-color: inherit
459 | // $jumbotron-font-size: ceil(($font-size-base * 1.5))
460 |
461 | //== Form states and alerts
462 | //
463 | //## Define colors for form feedback states and, by default, alerts.
464 |
465 | // $state-success-text: #3c763d
466 | // $state-success-bg: #dff0d8
467 | // $state-success-border: darken(adjust-hue($state-success-bg, -10), 5%)
468 |
469 | // $state-info-text: #31708f
470 | // $state-info-bg: #d9edf7
471 | // $state-info-border: darken(adjust-hue($state-info-bg, -10), 7%)
472 |
473 | // $state-warning-text: #8a6d3b
474 | // $state-warning-bg: #fcf8e3
475 | // $state-warning-border: darken(adjust-hue($state-warning-bg, -10), 5%)
476 |
477 | // $state-danger-text: #a94442
478 | // $state-danger-bg: #f2dede
479 | // $state-danger-border: darken(adjust-hue($state-danger-bg, -10), 5%)
480 |
481 | //== Tooltips
482 | //
483 | //##
484 |
485 | //* Tooltip max width
486 | // $tooltip-max-width: 200px
487 | //* Tooltip text color
488 | // $tooltip-color: #fff
489 | //* Tooltip background color
490 | // $tooltip-bg: #000
491 | // $tooltip-opacity: .9
492 |
493 | //* Tooltip arrow width
494 | // $tooltip-arrow-width: 5px
495 | //* Tooltip arrow color
496 | // $tooltip-arrow-color: $tooltip-bg
497 |
498 | //== Popovers
499 | //
500 | //##
501 |
502 | //* Popover body background color
503 | // $popover-bg: #fff
504 | //* Popover maximum width
505 | // $popover-max-width: 276px
506 | //* Popover border color
507 | // $popover-border-color: rgba(0,0,0,.2)
508 | //* Popover fallback border color
509 | // $popover-fallback-border-color: #ccc
510 |
511 | //* Popover title background color
512 | // $popover-title-bg: darken($popover-bg, 3%)
513 |
514 | //* Popover arrow width
515 | // $popover-arrow-width: 10px
516 | //* Popover arrow color
517 | // $popover-arrow-color: #fff
518 |
519 | //* Popover outer arrow width
520 | // $popover-arrow-outer-width: ($popover-arrow-width + 1)
521 | //* Popover outer arrow color
522 | // $popover-arrow-outer-color: rgba($popover-border-color, 0.05)
523 | //* Popover outer arrow fallback color
524 | // $popover-arrow-outer-fallback-color: darken($popover-fallback-border-color, 20%)
525 |
526 | //== Labels
527 | //
528 | //##
529 |
530 | //* Default label background color
531 | $label-default-bg: $gray-dark;
532 | //* Primary label background color
533 | // $label-primary-bg: $brand-primary
534 | //* Success label background color
535 | // $label-success-bg: $brand-success
536 | //* Info label background color
537 | // $label-info-bg: $brand-info
538 | //* Warning label background color
539 | // $label-warning-bg: $brand-warning
540 | //* Danger label background color
541 | // $label-danger-bg: $brand-danger
542 |
543 | //* Default label text color
544 | // $label-color: #fff
545 | //* Default text color of a linked label
546 | // $label-link-hover-color: #fff
547 |
548 | //== Modals
549 | //
550 | //##
551 |
552 | //* Padding applied to the modal body
553 | // $modal-inner-padding: 15px
554 |
555 | //* Padding applied to the modal title
556 | // $modal-title-padding: 15px
557 | //* Modal title line-height
558 | // $modal-title-line-height: $line-height-base
559 |
560 | //* Background color of modal content area
561 | // $modal-content-bg: #fff
562 | //* Modal content border color
563 | // $modal-content-border-color: rgba(0,0,0,.2)
564 | //* Modal content border color **for IE8**
565 | // $modal-content-fallback-border-color: #999
566 |
567 | //* Modal backdrop background color
568 | // $modal-backdrop-bg: #000
569 | //* Modal backdrop opacity
570 | // $modal-backdrop-opacity: .5
571 | //* Modal header border color
572 | // $modal-header-border-color: #e5e5e5
573 | //* Modal footer border color
574 | // $modal-footer-border-color: $modal-header-border-color
575 |
576 | // $modal-lg: 900px
577 | // $modal-md: 600px
578 | // $modal-sm: 300px
579 |
580 | //== Alerts
581 | //
582 | //## Define alert colors, border radius, and padding.
583 |
584 | // $alert-padding: 15px
585 | $alert-border-radius: $border-radius-base;
586 | // $alert-link-font-weight: bold
587 |
588 | // $alert-success-bg: $state-success-bg
589 | // $alert-success-text: $state-success-text
590 | // $alert-success-border: $state-success-border
591 |
592 | // $alert-info-bg: $state-info-bg
593 | // $alert-info-text: $state-info-text
594 | // $alert-info-border: $state-info-border
595 |
596 | // $alert-warning-bg: $state-warning-bg
597 | // $alert-warning-text: $state-warning-text
598 | // $alert-warning-border: $state-warning-border
599 |
600 | // $alert-danger-bg: $state-danger-bg
601 | // $alert-danger-text: $state-danger-text
602 | // $alert-danger-border: $state-danger-border
603 |
604 | //== Progress bars
605 | //
606 | //##
607 |
608 | //* Background color of the whole progress component
609 | // $progress-bg: #f5f5f5
610 | //* Progress bar text color
611 | // $progress-bar-color: #fff
612 |
613 | //* Default progress bar color
614 | // $progress-bar-bg: $brand-primary
615 | //* Success progress bar color
616 | // $progress-bar-success-bg: $brand-success
617 | //* Warning progress bar color
618 | // $progress-bar-warning-bg: $brand-warning
619 | //* Danger progress bar color
620 | // $progress-bar-danger-bg: $brand-danger
621 | //* Info progress bar color
622 | // $progress-bar-info-bg: $brand-info
623 |
624 | //== List group
625 | //
626 | //##
627 |
628 | //* Background color on `.list-group-item`
629 | // $list-group-bg: #fff
630 | //* `.list-group-item` border color
631 | // $list-group-border: #ddd
632 | //* List group border radius
633 | // $list-group-border-radius: $border-radius-base
634 |
635 | //* Background color of single list items on hover
636 | // $list-group-hover-bg: #f5f5f5
637 | //* Text color of active list items
638 | // $list-group-active-color: $component-active-color
639 | //* Background color of active list items
640 | // $list-group-active-bg: $component-active-bg
641 | //* Border color of active list elements
642 | // $list-group-active-border: $list-group-active-bg
643 | //* Text color for content within active list items
644 | // $list-group-active-text-color: lighten($list-group-active-bg, 40%)
645 |
646 | //* Text color of disabled list items
647 | // $list-group-disabled-color: $gray-light
648 | //* Background color of disabled list items
649 | // $list-group-disabled-bg: $gray-lighter
650 | //* Text color for content within disabled list items
651 | // $list-group-disabled-text-color: $list-group-disabled-color
652 |
653 | // $list-group-link-color: #555
654 | // $list-group-link-hover-color: $list-group-link-color
655 | // $list-group-link-heading-color: #333
656 |
657 | //== Panels
658 | //
659 | //##
660 |
661 | // $panel-bg: #fff
662 | // $panel-body-padding: 15px
663 | // $panel-heading-padding: 10px 15px
664 | // $panel-footer-padding: $panel-heading-padding
665 | $panel-border-radius: $border-radius-base;
666 |
667 | //* Border color for elements within panels
668 | // $panel-inner-border: #ddd
669 | // $panel-footer-bg: #f5f5f5
670 |
671 | // $panel-default-text: $gray-dark
672 | // $panel-default-border: #ddd
673 | // $panel-default-heading-bg: #f5f5f5
674 |
675 | // $panel-primary-text: #fff
676 | // $panel-primary-border: $brand-primary
677 | // $panel-primary-heading-bg: $brand-primary
678 |
679 | // $panel-success-text: $state-success-text
680 | // $panel-success-border: $state-success-border
681 | // $panel-success-heading-bg: $state-success-bg
682 |
683 | // $panel-info-text: $state-info-text
684 | // $panel-info-border: $state-info-border
685 | // $panel-info-heading-bg: $state-info-bg
686 |
687 | // $panel-warning-text: $state-warning-text
688 | // $panel-warning-border: $state-warning-border
689 | // $panel-warning-heading-bg: $state-warning-bg
690 |
691 | // $panel-danger-text: $state-danger-text
692 | // $panel-danger-border: $state-danger-border
693 | // $panel-danger-heading-bg: $state-danger-bg
694 |
695 | //== Thumbnails
696 | //
697 | //##
698 |
699 | //* Padding around the thumbnail image
700 | // $thumbnail-padding: 4px
701 | //* Thumbnail background color
702 | // $thumbnail-bg: $body-bg
703 | //* Thumbnail border color
704 | $thumbnail-border: none;
705 | //* Thumbnail border radius
706 | $thumbnail-border-radius: $border-radius-base;
707 |
708 | //* Custom text color for thumbnail captions
709 | // $thumbnail-caption-color: $text-color
710 | //* Padding around the thumbnail caption
711 | // $thumbnail-caption-padding: 9px
712 |
713 | //== Wells
714 | //
715 | //##
716 |
717 | // $well-bg: #f5f5f5
718 | // $well-border: darken($well-bg, 7%)
719 |
720 | //== Badges
721 | //
722 | //##
723 |
724 | // $badge-color: #fff
725 | //* Linked badge text color on hover
726 | // $badge-link-hover-color: #fff
727 | // $badge-bg: $gray-light
728 |
729 | //* Badge text color in active nav link
730 | // $badge-active-color: $link-color
731 | //* Badge background color in active nav link
732 | // $badge-active-bg: #fff
733 |
734 | // $badge-font-weight: bold
735 | // $badge-line-height: 1
736 | // $badge-border-radius: 10px
737 |
738 | //== Breadcrumbs
739 | //
740 | //##
741 |
742 | // $breadcrumb-padding-vertical: 8px
743 | // $breadcrumb-padding-horizontal: 15px
744 | //* Breadcrumb background color
745 | $breadcrumb-bg: none;
746 | //* Breadcrumb text color
747 | $breadcrumb-color: #fff;
748 | //* Text color of current page in the breadcrumb
749 | $breadcrumb-active-color: $brand-primary;
750 | //* Textual separator for between breadcrumb elements
751 | $breadcrumb-separator: "\f054"
752 |
753 | //== Carousel
754 | //
755 | //##
756 |
757 | // $carousel-text-shadow: 0 1px 2px rgba(0,0,0,.6)
758 |
759 | // $carousel-control-color: #fff
760 | // $carousel-control-width: 15%
761 | // $carousel-control-opacity: .5
762 | // $carousel-control-font-size: 20px
763 |
764 | // $carousel-indicator-active-bg: #fff
765 | // $carousel-indicator-border-color: #fff
766 |
767 | // $carousel-caption-color: #fff
768 |
769 | //== Close
770 | //
771 | //##
772 |
773 | // $close-font-weight: bold
774 | // $close-color: #000
775 | // $close-text-shadow: 0 1px 0 #fff
776 |
777 | //== Code
778 | //
779 | //##
780 |
781 | // $code-color: #c7254e
782 | // $code-bg: #f9f2f4
783 |
784 | // $kbd-color: #fff
785 | // $kbd-bg: #333
786 |
787 | // $pre-bg: #f5f5f5
788 | // $pre-color: $gray-dark
789 | // $pre-border-color: #ccc
790 | // $pre-scrollable-max-height: 340px
791 |
792 | //== Type
793 | //
794 | //##
795 |
796 | //* Text muted color
797 | // $text-muted: $gray-light
798 | //* Abbreviations and acronyms border color
799 | // $abbr-border-color: $gray-light
800 | //* Headings small color
801 | // $headings-small-color: $gray-light
802 | //* Blockquote small color
803 | // $blockquote-small-color: $gray-light
804 | //* Blockquote font size
805 | // $blockquote-font-size: ($font-size-base * 1.25)
806 | //* Blockquote border color
807 | // $blockquote-border-color: $gray-lighter
808 | //* Page header border color
809 | // $page-header-border-color: $gray-lighter
810 |
811 | //== Miscellaneous
812 | //
813 | //##
814 |
815 | //* Horizontal line color.
816 | // $hr-border: $gray-lighter
817 |
818 | //* Horizontal offset for forms and lists.
819 | // $component-offset-horizontal: 180px
820 |
--------------------------------------------------------------------------------
/content/assets/css/styles.scss:
--------------------------------------------------------------------------------
1 | // customize bootstrap variables here:
2 | @import "fonts";
3 | @import "variables";
4 | @import "bootstrap";
5 | @import "search";
6 |
7 | $max-width: $container-lg;
8 |
9 | /* main layout */
10 | html {
11 | width: 100%;
12 | overflow: hidden;
13 | overflow-y: scroll;
14 |
15 | /* poor support - but for the future! */
16 | hyphens: auto;
17 | -webkit-hyphens: auto;
18 | -moz-hyphens: auto;
19 | -ms-hyphens: auto;
20 | }
21 |
22 | body {
23 | padding-top: $navbar-height;
24 | width: 100%;
25 | overflow: hidden;
26 | overflow-y: initial;
27 | }
28 |
29 | .top-space {
30 | padding-top: 40px;
31 | }
32 |
33 | h1 {
34 | color: $gray-dark;
35 | margin-top: 60px;
36 | line-height: 0.9em;
37 | }
38 |
39 | h1 + h2 {
40 | margin-top: -0.2em;
41 | }
42 |
43 | h2 {
44 | margin-top: 1.2em;
45 | .date {
46 | margin-right: 0.2em;
47 | color: $brand-primary;
48 | font-size: 16px;
49 | }
50 | }
51 |
52 | p {
53 | margin-bottom: 0.5em;
54 | .fa {
55 | display: inline;
56 | color: $gray-dark;
57 | font-size: 1.1em;
58 | }
59 | }
60 |
61 | i {
62 | font-style: normal;
63 | font-family: FontAwesome;
64 | }
65 |
66 | .container, .container-fluid {
67 | max-width: $max-width;
68 | }
69 |
70 | .label {
71 | display: inline-block;
72 | border-radius: $border-radius-base;
73 | padding-top: 0.3em;
74 | padding-bottom: 0.1em;
75 | font-size: 14px;
76 | font-weight: normal;
77 | margin-right: 2px;
78 | margin-bottom: $padding-small-vertical;
79 | }
80 |
81 | /* navigation bar */
82 | .navbar {
83 | border: none;
84 |
85 | .navbar-brand {
86 | font-size: 24px;
87 | padding: 12px 15px;
88 | }
89 |
90 | .btn.btn-default {
91 | padding: 0.2em;
92 | height: 1.8em;
93 | max-width: 2em;
94 | &:hover, &:focus, &:active {
95 | color: $brand-primary;
96 | background-color: #fff;
97 | border-color: #fff;
98 | }
99 | }
100 |
101 | .navbar-form {
102 | margin-top: 0.8em;
103 | margin-bottom: 0.8em;
104 | }
105 |
106 | input {
107 | height: 1.8em;
108 | padding-top: 6px;
109 | }
110 |
111 | .fa {
112 | min-width: 20px;
113 | font-size: 1.3em;
114 | }
115 | }
116 |
117 | /* breadcrumb */
118 | #breadcrumb {
119 | background-color: $gray;
120 | font-size: 16px;
121 | ol {
122 | max-width: $max-width;
123 | padding: $padding-small-vertical $padding-large-horizontal;
124 | margin-bottom: 0;
125 | li + li:before {
126 | font-family: FontAwesome;
127 | font-size: 9px;
128 | }
129 | }
130 | }
131 |
132 | /* browse-by-buttons at homescreen */
133 | .browse-button-bar {
134 | text-align: center;
135 | margin-top: $padding-large-vertical;
136 | margin-bottom: $padding-large-vertical;
137 | .btn-lg {
138 | margin: $padding-small-vertical $padding-small-horizontal;
139 | }
140 | .fa {
141 | font-size: 1.3em;
142 | }
143 | }
144 |
145 | /* news */
146 | h1 + .news-item {
147 | margin-top: -0.5em;
148 | }
149 |
150 | /* recording detail site */
151 | .nav.nav-tabs.nav-justified {
152 | margin-top: 16px;
153 | font-weight: bold;
154 | font-size: 24px;
155 | background-color: $gray;
156 |
157 | & > li {
158 | a {
159 | padding: $padding-large-vertical 0 0 0;
160 | margin-bottom: 0;
161 | border: none;
162 | &:hover, &:focus, &:active {
163 | background-color: #fff;
164 | }
165 | &:hover, &:focus, &:active {
166 | border: none;
167 | }
168 | }
169 | &.active > a {
170 | color: white;
171 | background-color: $gray-dark;
172 | }
173 | }
174 | }
175 |
176 | .tab-content {
177 | background-color: $gray-dark;
178 |
179 | .tab-pane {
180 | display: none;
181 | &.active {
182 | display: block;
183 | }
184 | }
185 |
186 | #video {
187 | text-align: center;
188 | .video {
189 | margin: auto;
190 | }
191 | * {
192 | max-width: 100%;
193 | }
194 | }
195 |
196 | #audio * {
197 | max-width: 100%;
198 | }
199 |
200 | #download {
201 | padding: $padding-large-horizontal;
202 | h3 {
203 | color: $gray-light;
204 | &:first-child {
205 | margin-top: 0;
206 | }
207 | }
208 | ul {
209 | list-style-type: none;
210 | padding: 0;
211 | li {
212 | min-width: 49%;
213 | display: inline-block;
214 | }
215 | a {
216 | margin: 0 $padding-xs-horizontal;
217 | }
218 | }
219 | .filetype {
220 | text-transform: uppercase;
221 | margin-right: $padding-small-horizontal;
222 | color: #000;
223 | background-color: #fff;
224 | min-width: 4em;
225 | }
226 | a:hover, a:focus, a:active {
227 | text-decoration: none;
228 | }
229 | }
230 |
231 | #embedshare {
232 | padding: $padding-large-horizontal;
233 | input {
234 | border: 1px solid #d87500;
235 | color: #d87500;
236 | border-width: 1px;
237 | background-color: #555;
238 | }
239 | h3 {
240 | color: $gray-light;
241 | &:first-child {
242 | margin-top: 0;
243 | }
244 | }
245 | ul {
246 | list-style-type: none;
247 | padding: 0;
248 | li {
249 | min-width: 49%;
250 | display: inline-block;
251 | }
252 | a {
253 | margin: 0 $padding-xs-horizontal;
254 | }
255 | }
256 | a:hover, a:focus, a:active {
257 | text-decoration: none;
258 | }
259 | i {
260 | color: #d87500;
261 | }
262 | }
263 | }
264 |
265 | ul.metadata {
266 | list-style: none;
267 | padding: $padding-small-vertical 0;
268 | text-align: center;
269 | border-bottom: 1px solid $gray;
270 | li {
271 | display: inline-block;
272 | min-width: 24.5%;
273 | padding: $padding-small-vertical $padding-small-horizontal;
274 | }
275 | .fa {
276 | margin-right: $padding-xs-horizontal;
277 | }
278 | }
279 |
280 | /* footer */
281 | footer {
282 | padding: 0.7em;
283 | margin-top: 2em;
284 | color: $gray-light;
285 | background-color: $gray-dark;
286 | text-align: center;
287 | }
288 |
289 | /* links */
290 | a.inverted {
291 | color: $gray-lighter;
292 | }
293 |
294 | /* buttons */
295 | .btn-lg {
296 | padding-top: 5px;
297 | padding-bottom: 5px;
298 | .fa {
299 | /* nicely positioned icons on large buttons */
300 | position: relative;
301 | top: 0.15em;
302 | margin-right: 0.3em;
303 | }
304 | }
305 |
306 | /* promoted videos */
307 | .promoted {
308 | width: 100%;
309 | height: 300px;
310 | overflow: hidden;
311 | text-align: center;
312 | background-image: url('/images/promoted_bg.jpg');
313 | .carousel.slide {
314 | height: 290px;
315 | position: relative;
316 | top: -112px;
317 | }
318 | .carousel-inner {
319 | height: 100%;
320 | }
321 | .item:hover {
322 | text-decoration: none;
323 | }
324 | .item img {
325 | height: 190px;
326 | display: inline-block;
327 | margin-top: 12px;
328 | border: 1px solid $transparent-white;
329 | }
330 | .titlebar {
331 | width: 100%;
332 | height: 112px;
333 | background-color: $transparent-white;
334 | position: relative;
335 | top: 188px;
336 | }
337 | .carousel-caption {
338 | position: relative;
339 | top: 0;
340 | height: 90px;
341 | overflow: hidden;
342 | left: initial;
343 | right: initial;
344 | padding: 0;
345 | }
346 | .title {
347 | color: white;
348 | margin: 0.7em;
349 | margin-bottom: 0;
350 | }
351 | .subtitle {
352 | font-size: 18px;
353 | color: $brand-primary;
354 | margin: 0 0.8em;
355 | text-overflow: ellipsis;
356 | white-space: nowrap;
357 | overflow: hidden;
358 | }
359 | }
360 |
361 | /* browse folders and conferences */
362 | .thumbnail {
363 | display: block;
364 | float: left;
365 | text-align: center;
366 | font-size: 16px;
367 | min-width: 130px;
368 | margin: 5px;
369 | height: 135px;
370 |
371 | .header {
372 | text-align: left;
373 | }
374 |
375 | &:hover, &:focus, &:active {
376 | text-decoration: none;
377 | }
378 |
379 | &.folder {
380 | background-color: $brand-primary;
381 | color: white;
382 |
383 | .header {
384 | margin-bottom: $padding-large-vertical;
385 | }
386 |
387 | .fa.fa-folder {
388 | font-size: 40px;
389 | padding: 10px 0;
390 | }
391 |
392 | &:hover, &:focus, &:active {
393 | background-color: $gray-dark;
394 | }
395 |
396 | .caption {
397 | color: white;
398 | }
399 | }
400 |
401 | &.conference {
402 | color: $gray-dark;
403 |
404 | img {
405 | height: 64px;
406 | margin: $padding-small-vertical auto;
407 | }
408 |
409 | .caption {
410 | color: white;
411 | background-color: $gray-dark;
412 | -webkit-transition: all 0.2s ease-in-out;
413 | -o-transition: all 0.2s ease-in-out;
414 | transition: all 0.2s ease-in-out;
415 | }
416 |
417 | &:hover, &:focus, &:active {
418 | color: $brand-primary;
419 | .caption {
420 | background-color: $brand-primary;
421 | }
422 | }
423 | }
424 | }
425 |
426 | /* events overview */
427 | .event-previews, .event-previews-tags, .event-previews-search {
428 | .event-preview {
429 | margin: 16px 0;
430 | display: block;
431 | clear: both;
432 | height: 120px;
433 |
434 | .video-thumbnail {
435 | float: left;
436 | height: 120px;
437 | width: 160px;
438 | }
439 |
440 | color: black;
441 | &:hover, &:focus, &:active {
442 | text-decoration: none;
443 | .caption h3, .metadata li {
444 | color: $link-color;
445 | }
446 | }
447 |
448 | .metadata {
449 | text-align: left;
450 | border: none;
451 | li {
452 | padding: $padding-xs-vertical;
453 | padding-right: $padding-xs-horizontal;
454 | color: $gray-dark;
455 | min-width: inherit;
456 | }
457 | }
458 |
459 | .caption {
460 | margin-left: 160px;
461 | padding-left: $padding-large-horizontal;
462 | h3 {
463 | margin-top: 0;
464 | color: black;
465 | }
466 | }
467 | }
468 | }
469 |
470 | .events-header {
471 | clear: both;
472 | img {
473 | float: left;
474 | max-height: 40px;
475 | margin-right: $padding-small-horizontal;
476 | }
477 | #sorting {
478 | margin: $padding-large-vertical 0;
479 | float: right;
480 | }
481 | }
482 |
483 | /* event overview for tags */
484 | .event-previews-tags .event-preview,
485 | .event-previews-search .event-preview {
486 | height: auto;
487 | overflow: hidden;
488 |
489 | /* search results numbering */
490 | h3 .number::after {
491 | content: '.';
492 | }
493 | .conference-logo {
494 | float: left;
495 | width: 100px;
496 | height: auto;
497 | }
498 | .caption {
499 | margin-left: 100px;
500 | }
501 |
502 | .metadata {
503 | margin-bottom: 0;
504 | }
505 | }
506 |
507 |
508 | .search {
509 | h1 {
510 | margin-bottom: 10px;
511 | }
512 | .statistics {
513 | margin-top: 15px;
514 | display: none;
515 | &.visible {
516 | display: block;
517 | }
518 | }
519 | .paging {
520 | display: none;
521 |
522 | clear: both;
523 | font-size: 24px;
524 | margin: 50px 0 0 80px;
525 |
526 | &.visible {
527 | display: block;
528 | }
529 |
530 | li {
531 | display: block;
532 | text-align: center;
533 | float: left;
534 | list-style-type: none;
535 | padding: 0 5px;
536 |
537 | a:hover {
538 | text-decoration: none;
539 | }
540 |
541 | &.next, &.prev {
542 | display: none;
543 | &.visible { display: block; }
544 | }
545 | &.next { margin-left: 12px; }
546 | &.prev { margin-right: 12px; }
547 | &.active {
548 | background-color: #555;
549 | border-radius: 5px;
550 | a, a:hover {
551 | color: #eee;
552 | }
553 | }
554 | }
555 | }
556 |
557 | /* search results listing */
558 | ol.event-previews-search {
559 | list-style-type: none;
560 | &, >li {
561 | margin: 0;
562 | padding: 0;
563 | }
564 |
565 | >li.no-results {
566 | margin: 30px 0;
567 | }
568 | }
569 | }
570 |
571 | /* Font classes - Tags */
572 | .tag-cloud { margin-top: 1.5em; }
573 | .tag-cloud .xtiny { font-size: 0.9em; text-decoration:none; }
574 | .tag-cloud .tiny { font-size: 1.1em; font-weight:lighter; }
575 | .tag-cloud .normal{ font-size: 1.3em; text-decoration:none; }
576 | .tag-cloud .large { font-size: 1.5em; text-decoration:none; }
577 | .tag-cloud .xlarge { font-size: 1.75em; font-weight:bold; }
578 |
579 |
580 | /* and now for medium screens: */
581 | @media all and (min-width: $screen-sm-min) and (max-width: $container-desktop) {
582 |
583 | /* events overview */
584 | .event-previews .event-preview {
585 | h3 {
586 | font-size: 17px;
587 | }
588 | }
589 |
590 | }
591 |
592 | /* and now for small screens: */
593 | @media all and (max-width: $screen-xs-max) {
594 |
595 | /* main layout */
596 | body {
597 | padding-top: 0;
598 | }
599 |
600 | h1 {
601 | font-size: 34px;
602 | margin-top: 30px;
603 | }
604 | h2 {
605 | font-size: 23px;
606 | }
607 |
608 | /* large buttons */
609 | .btn-lg, .btn-group-lg>.btn {
610 | font-size: 23px;
611 | }
612 |
613 | /* navigation bar */
614 | .navbar {
615 | position: initial;
616 | & > .container-fluid {
617 | display: inline-table;
618 | width: 100%;
619 | }
620 | form {
621 | width: 100%;
622 | }
623 | .navbar-form.navbar-right:last-child {
624 | margin-right: 0;
625 | }
626 | .navbar-header, .navbar-form {
627 | padding: 0;
628 | margin-top: 0;
629 | margin-bottom: 0;
630 | min-width: 95px;
631 | }
632 | .input-group {
633 | margin-bottom: $padding-small-vertical;
634 | }
635 | .button-wrapper {
636 | text-align: right;
637 | margin-top: $padding-large-vertical;
638 | }
639 | }
640 |
641 | /* promoted videos */
642 | .promoted {
643 | height: 230px;
644 | .item img {
645 | height: 120px;
646 | }
647 | .titlebar {
648 | top: 118px;
649 | }
650 | }
651 |
652 | /* events overview */
653 | .event-preview {
654 | /* one column layout */
655 | width: 100%;
656 | }
657 |
658 | /* tabs */
659 | .nav.nav-tabs.nav-justified > li {
660 | a {
661 | font-size: 20px;
662 | padding-top: 5px;
663 | }
664 | &.active > a {
665 | background-color: $gray;
666 | }
667 | }
668 |
669 | /* events overview */
670 | .event-previews {
671 | .event-preview {
672 | height: auto;
673 |
674 | .video-thumbnail {
675 | margin-top: 1.5em;
676 | height: 90px;
677 | width: 120px;
678 | }
679 |
680 | .caption {
681 | margin-left: 120px;
682 | padding-left: $padding-small-horizontal;
683 | h3 {
684 | height: 90px;
685 | vertical-align: middle;
686 | display: table-cell;
687 | margin-top: 0;
688 | font-size: 17px;
689 | }
690 | }
691 | .metadata {
692 | position: relative;
693 | left: -130px;
694 | padding: 0;
695 | margin-bottom: 3px;
696 | white-space: nowrap;
697 | margin-bottom: $padding-small-vertical;
698 | }
699 | }
700 | }
701 |
702 | /* event overview for searches & tags */
703 | .event-previews-tags .event-preview,
704 | .event-previews-search .event-preview {
705 | h3 {
706 | font-size: 17px;
707 | }
708 | .conference-logo {
709 | width: 60px;
710 | }
711 | .caption {
712 | margin-left: 60px;
713 | }
714 | }
715 | }
716 |
717 | .mejs-overlay-loading { background: none !important; }
718 |
--------------------------------------------------------------------------------
/content/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/content/favicon-16x16.png
--------------------------------------------------------------------------------
/content/favicon-196x196.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/content/favicon-196x196.png
--------------------------------------------------------------------------------
/content/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/content/favicon-32x32.png
--------------------------------------------------------------------------------
/content/favicon-96x96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/content/favicon-96x96.png
--------------------------------------------------------------------------------
/content/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/content/favicon.ico
--------------------------------------------------------------------------------
/content/index.haml:
--------------------------------------------------------------------------------
1 | ---
2 | title: home
3 | ---
4 |
5 | = render '/partials/promoted'
6 |
7 | %div.container-fluid
8 | .row.browse-button-bar
9 | %a.btn.btn-primary.btn-lg{role: 'button', href: '/tags'}
10 | %span.fa.fa-tags
11 | Browse by tag
12 |
13 | %a.btn.btn-primary.btn-lg{role: 'button', href: '/browse'}
14 | %span.fa.fa-th
15 | Browse by category
16 |
17 | %h1 News
18 |
19 | - News.recent(10).each do |news|
20 | %article.news-item
21 | %h2
22 | %span.date= news.date_formatted
23 | = news.title
24 | =news.body
25 |
26 |
--------------------------------------------------------------------------------
/content/search.haml:
--------------------------------------------------------------------------------
1 | ---
2 | title: Search
3 | ---
4 |
5 | = render 'partials/search', :title => 'media.ccc.de-Search'
--------------------------------------------------------------------------------
/content/sitemap.haml:
--------------------------------------------------------------------------------
1 | = xml_sitemap
2 |
--------------------------------------------------------------------------------
/content/tags/index.haml:
--------------------------------------------------------------------------------
1 | ---
2 | title: Browse by tags
3 | ---
4 |
5 | %div.container-fluid
6 | %h1 Browse by tags
7 | .tag-cloud
8 | - tag_cloud.each do |tag|
9 | = tag
--------------------------------------------------------------------------------
/images/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/.gitkeep
--------------------------------------------------------------------------------
/images/audio_ready_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/audio_ready_icon.png
--------------------------------------------------------------------------------
/images/dvd_ready_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/dvd_ready_icon.png
--------------------------------------------------------------------------------
/images/folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/folder.png
--------------------------------------------------------------------------------
/images/hd_ready_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/hd_ready_icon.png
--------------------------------------------------------------------------------
/images/logos/blinkenlights/folder-blinkenlights.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/blinkenlights/folder-blinkenlights.jpg
--------------------------------------------------------------------------------
/images/logos/broadcast/sendezentrum/sendezentrum.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/broadcast/sendezentrum/sendezentrum.png
--------------------------------------------------------------------------------
/images/logos/chaosradio/video/chaosradio-logo-transparent-300.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/chaosradio/video/chaosradio-logo-transparent-300.png
--------------------------------------------------------------------------------
/images/logos/conferences/camp1999/camprocket.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/conferences/camp1999/camprocket.png
--------------------------------------------------------------------------------
/images/logos/conferences/camp2003/folder-camp-2003.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/conferences/camp2003/folder-camp-2003.png
--------------------------------------------------------------------------------
/images/logos/conferences/camp2007/folder-camp-2007.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/conferences/camp2007/folder-camp-2007.jpg
--------------------------------------------------------------------------------
/images/logos/conferences/camp2011/folder-camp-2011.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/conferences/camp2011/folder-camp-2011.png
--------------------------------------------------------------------------------
/images/logos/conferences/camp2015/camp15-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/conferences/camp2015/camp15-logo.png
--------------------------------------------------------------------------------
/images/logos/conferences/chaoscologne/1c2/1c2_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/conferences/chaoscologne/1c2/1c2_logo.png
--------------------------------------------------------------------------------
/images/logos/conferences/eh2014/logo-navbar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/conferences/eh2014/logo-navbar.png
--------------------------------------------------------------------------------
/images/logos/conferences/eh2015/eh15v4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/conferences/eh2015/eh15v4.png
--------------------------------------------------------------------------------
/images/logos/conferences/fiffkon/2014/fiffkon14-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/conferences/fiffkon/2014/fiffkon14-logo.png
--------------------------------------------------------------------------------
/images/logos/conferences/froscon/2010/folder-froscon-2010.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/conferences/froscon/2010/folder-froscon-2010.png
--------------------------------------------------------------------------------
/images/logos/conferences/froscon/2011/folder-froscon-2010.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/conferences/froscon/2011/folder-froscon-2010.png
--------------------------------------------------------------------------------
/images/logos/conferences/froscon/2012/froscon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/conferences/froscon/2012/froscon.png
--------------------------------------------------------------------------------
/images/logos/conferences/froscon/2013/folder-froscon-2010.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/conferences/froscon/2013/folder-froscon-2010.png
--------------------------------------------------------------------------------
/images/logos/conferences/froscon/2014/folder-froscon-2010.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/conferences/froscon/2014/folder-froscon-2010.png
--------------------------------------------------------------------------------
/images/logos/conferences/froscon/2015/froscon2015.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/conferences/froscon/2015/froscon2015.png
--------------------------------------------------------------------------------
/images/logos/conferences/har2009/folder-har-2009.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/conferences/har2009/folder-har-2009.png
--------------------------------------------------------------------------------
/images/logos/conferences/mrmcd/mrmcd0x8/folder-mrmcd-2009.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/conferences/mrmcd/mrmcd0x8/folder-mrmcd-2009.jpg
--------------------------------------------------------------------------------
/images/logos/conferences/mrmcd/mrmcd101b/folder-mrmcd-2006.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/conferences/mrmcd/mrmcd101b/folder-mrmcd-2006.png
--------------------------------------------------------------------------------
/images/logos/conferences/mrmcd/mrmcd111b/folder-mrmcd-2008.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/conferences/mrmcd/mrmcd111b/folder-mrmcd-2008.jpg
--------------------------------------------------------------------------------
/images/logos/conferences/mrmcd/mrmcd13/mrmcd2013-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/conferences/mrmcd/mrmcd13/mrmcd2013-logo.png
--------------------------------------------------------------------------------
/images/logos/conferences/mrmcd/mrmcd14/mrmcd_14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/conferences/mrmcd/mrmcd14/mrmcd_14.png
--------------------------------------------------------------------------------
/images/logos/conferences/mrmcd/mrmcd15/mrmcd_15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/conferences/mrmcd/mrmcd15/mrmcd_15.png
--------------------------------------------------------------------------------
/images/logos/conferences/sigint09/folder-sigint-2009.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/conferences/sigint09/folder-sigint-2009.png
--------------------------------------------------------------------------------
/images/logos/conferences/sigint10/folder-sigint-2010.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/conferences/sigint10/folder-sigint-2010.png
--------------------------------------------------------------------------------
/images/logos/conferences/sigint12/folder-sigint-2012.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/conferences/sigint12/folder-sigint-2012.jpg
--------------------------------------------------------------------------------
/images/logos/conferences/sigint13/folder-sigint-2013.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/conferences/sigint13/folder-sigint-2013.png
--------------------------------------------------------------------------------
/images/logos/congress/2000/realmedia/folder-17c3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/congress/2000/realmedia/folder-17c3.png
--------------------------------------------------------------------------------
/images/logos/congress/2001/folder-18c3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/congress/2001/folder-18c3.png
--------------------------------------------------------------------------------
/images/logos/congress/2002/folder-19c3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/congress/2002/folder-19c3.png
--------------------------------------------------------------------------------
/images/logos/congress/2003/folder-20c3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/congress/2003/folder-20c3.png
--------------------------------------------------------------------------------
/images/logos/congress/2004/folder-21c3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/congress/2004/folder-21c3.png
--------------------------------------------------------------------------------
/images/logos/congress/2005/folder-22c3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/congress/2005/folder-22c3.gif
--------------------------------------------------------------------------------
/images/logos/congress/2006/folder-23c3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/congress/2006/folder-23c3.png
--------------------------------------------------------------------------------
/images/logos/congress/2007/folder-24c3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/congress/2007/folder-24c3.jpg
--------------------------------------------------------------------------------
/images/logos/congress/2008/folder-25c3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/congress/2008/folder-25c3.png
--------------------------------------------------------------------------------
/images/logos/congress/2009/folder-26c3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/congress/2009/folder-26c3.jpg
--------------------------------------------------------------------------------
/images/logos/congress/2010/folder-27c3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/congress/2010/folder-27c3.png
--------------------------------------------------------------------------------
/images/logos/congress/2011/folder-28c3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/congress/2011/folder-28c3.png
--------------------------------------------------------------------------------
/images/logos/congress/2012/folder-29c3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/congress/2012/folder-29c3.png
--------------------------------------------------------------------------------
/images/logos/congress/2013/folder-30c3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/congress/2013/folder-30c3.png
--------------------------------------------------------------------------------
/images/logos/congress/2014/folder-2014.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/congress/2014/folder-2014.png
--------------------------------------------------------------------------------
/images/logos/events/Panoptische_Prinzip/logo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/events/Panoptische_Prinzip/logo.jpg
--------------------------------------------------------------------------------
/images/logos/events/cryptocon/2015/cc15_logo_transparent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/events/cryptocon/2015/cc15_logo_transparent.png
--------------------------------------------------------------------------------
/images/logos/events/datenspuren/2014/datenspuren2014-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/events/datenspuren/2014/datenspuren2014-logo.png
--------------------------------------------------------------------------------
/images/logos/events/datenspuren/2015/ds2015.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/events/datenspuren/2015/ds2015.png
--------------------------------------------------------------------------------
/images/logos/events/gpn/gpn14/80px-Gpn14_logo_navi.jpg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/events/gpn/gpn14/80px-Gpn14_logo_navi.jpg.png
--------------------------------------------------------------------------------
/images/logos/events/gpn/gpn15/gpn15_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/events/gpn/gpn15/gpn15_logo.png
--------------------------------------------------------------------------------
/images/logos/events/gpn10/95px-GPN10-TShirt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/events/gpn10/95px-GPN10-TShirt.png
--------------------------------------------------------------------------------
/images/logos/events/hackover/2013/hackover-2013.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/events/hackover/2013/hackover-2013.png
--------------------------------------------------------------------------------
/images/logos/events/hackover/2014/hackover-2014.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/events/hackover/2014/hackover-2014.png
--------------------------------------------------------------------------------
/images/logos/events/hackover/hackover-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/events/hackover/hackover-logo.png
--------------------------------------------------------------------------------
/images/logos/events/netzpolitik/10np/10np.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/events/netzpolitik/10np/10np.png
--------------------------------------------------------------------------------
/images/logos/events/netzpolitik/11np/11np.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/events/netzpolitik/11np/11np.png
--------------------------------------------------------------------------------
/images/logos/events/vcfb/2014/vcfb-logo-quer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/events/vcfb/2014/vcfb-logo-quer.png
--------------------------------------------------------------------------------
/images/logos/events/vcfb/2014/vcfb2015-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/events/vcfb/2014/vcfb2015-logo.png
--------------------------------------------------------------------------------
/images/logos/events/wauland/whs_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/events/wauland/whs_logo.png
--------------------------------------------------------------------------------
/images/logos/events/wie_werden_kriege_gemacht-koeln_2015/unknown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/events/wie_werden_kriege_gemacht-koeln_2015/unknown.png
--------------------------------------------------------------------------------
/images/logos/regional/berlin/datengarten/datengarten.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/regional/berlin/datengarten/datengarten.png
--------------------------------------------------------------------------------
/images/logos/regional/c4/openchaos/c4-logo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/regional/c4/openchaos/c4-logo.jpg
--------------------------------------------------------------------------------
/images/logos/regional/cccac/vortraege/cccac_logo_bw_kleiner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/regional/cccac/vortraege/cccac_logo_bw_kleiner.png
--------------------------------------------------------------------------------
/images/logos/regional/cccac/vortraege/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/regional/cccac/vortraege/logo.png
--------------------------------------------------------------------------------
/images/logos/unknown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/logos/unknown.png
--------------------------------------------------------------------------------
/images/miro-banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/miro-banner.png
--------------------------------------------------------------------------------
/images/promoted_bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/promoted_bg.jpg
--------------------------------------------------------------------------------
/images/rss_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/rss_logo.png
--------------------------------------------------------------------------------
/images/spinner.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/spinner.gif
--------------------------------------------------------------------------------
/images/tv-rausch.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/tv-rausch.gif
--------------------------------------------------------------------------------
/images/tv.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/images/tv.png
--------------------------------------------------------------------------------
/layouts/browse-download-page.haml:
--------------------------------------------------------------------------------
1 | !!! 5
2 | %html{lang: 'en'}
3 | %head
4 | = render '/partials/header'
5 | %title
6 | C3TV -
7 | = h @item[:title]
8 | %link{href: '/assets/css/mediaelementplayer.min.css', type: 'text/css', rel: 'stylesheet'}
9 | %body
10 | = render '/partials/navbar'
11 |
12 | %div.container-fluid
13 |
14 | %h1
15 | = h @item[:event].title
16 | - if @item[:event].subtitle
17 | %h2
18 | = h @item[:event].subtitle
19 |
20 | %ul.nav.nav-tabs.nav-justified{role: 'tablist'}
21 | %li
22 | %a{href: @item[:event].url+'#video', role: 'tab'} Video
23 | %li
24 | %a{href: @item[:event].url+'#audio', role: 'tab'} Audio
25 | %li.active
26 | %a{href: '#download', role: 'tab'} Download
27 |
28 | .tab-content
29 | #download.tab-pane.active{style: 'display: block'}
30 | = render '/partials/download'
31 |
32 | = render '/partials/footer'
33 |
--------------------------------------------------------------------------------
/layouts/browse-index.haml:
--------------------------------------------------------------------------------
1 | !!! 5
2 | %html{lang: 'en'}
3 | %head
4 | = render '/partials/header'
5 | %title
6 | C3TV -
7 | = h @item[:title]
8 | %body
9 | = render '/partials/navbar'
10 |
11 | .container-fluid
12 | %h1 Browse by category
13 |
14 | - if @item[:folders].present?
15 | - @item[:folders].each do |folder|
16 | - if folder.conference
17 | %a.thumbnail.conference{href: h(folder.url)}
18 | .header
19 | %span.fa.fa-video-camera
20 | = Event.recorded_at(folder.conference).to_a.count
21 | %img{src: folder.conference.logo_url, alt: 'conference logo'}
22 | .caption
23 | = folder.conference.title
24 | - else
25 | %a.thumbnail.folder{href: h(folder.url)}
26 | .header
27 | %span.fa.fa-th
28 | .fa.fa-folder
29 | .caption
30 | = folder.name
31 |
32 | = yield
33 |
34 | = render '/partials/footer'
35 |
--------------------------------------------------------------------------------
/layouts/browse-oembed-page.haml:
--------------------------------------------------------------------------------
1 | !!! 5
2 | %html{lang: 'en'}
3 | %head
4 | %title
5 | C3TV -
6 | = h @item[:title]
7 | %link{href: '/assets/css/mediaelementplayer.min.css', type: 'text/css', rel: 'stylesheet'}
8 | %body
9 | %div.container-fluid
10 | = render '/partials/videoplayer'
11 |
12 | %script(src='/assets/js/jquery.min.js')
13 | %script(src='/assets/js/bootstrap.min.js')
14 | %script(src='/assets/js/mediaelement-and-player.min.js')
15 |
16 | = render '/partials/videoplayer_js'
17 |
--------------------------------------------------------------------------------
/layouts/browse-show-folder.haml:
--------------------------------------------------------------------------------
1 | !!! 5
2 | %html{lang: 'en'}
3 | %head
4 | = render '/partials/header'
5 | %title
6 | C3TV -
7 | = h @item[:title]
8 | = render '/partials/folder-feeds'
9 | %body
10 | = render '/partials/navbar'
11 |
12 | .container-fluid
13 |
14 | .events-header
15 | %img{src: @item[:conference].logo_url, alt: 'conference logo'}
16 |
17 | %h1= @item[:conference].acronym.gsub(/[\-_]/,' ')
18 | - if @item[:conference].title and not item[:conference].acronym == @item[:conference].title
19 | %h2= h @item[:conference].title
20 |
21 | #sorting.btn-group.btn-group-sm.btn-group-justified
22 | - %w{name duration date}.each do |sorting|
23 | - if @item[:sorting] == sorting
24 | %a.btn.btn-primary.active{ href: "/browse/#{@item[:conference].webgen_location}/#{sorting}/" }= sorting
25 | - else
26 | %a.btn.btn-primary{ href: "/browse/#{@item[:conference].webgen_location}/#{sorting}/" }= sorting
27 |
28 | - if @item[:events].present?
29 | .event-previews
30 | -# Build event overview
31 | - @item[:events].each do |event|
32 | - recordings = event.recordings.downloaded
33 | %a.event-preview{href: h(event.url)}
34 | %img.video-thumbnail{src: h(event.thumb_url), alt: h(event.title)}
35 | .caption
36 | %ul.metadata
37 | %li
38 | %span.fa.fa-clock-o
39 | = recording_length(recordings)
40 | %li
41 | %span.fa.fa-calendar-o
42 | = date(event)
43 | %li.persons
44 | %span.fa{class: event.persons_icon}
45 | = event.persons_text
46 | %h3= h event.title
47 |
48 | = yield
49 |
50 | = render '/partials/footer'
51 |
--------------------------------------------------------------------------------
/layouts/browse-show-page.haml:
--------------------------------------------------------------------------------
1 | !!! 5
2 | %html{lang: 'en'}
3 | %head
4 | = render '/partials/header'
5 | %title
6 | C3TV -
7 | = h @item[:title]
8 | = render '/partials/folder-feeds'
9 | %link{href: '/assets/css/mediaelementplayer.min.css', type: 'text/css', rel: 'stylesheet'}
10 | != ""
11 | %body
12 | %script(src='/assets/js/jquery.min.js')
13 | = render '/partials/navbar'
14 |
15 | %div.container-fluid
16 | %h1
17 | = h @item[:event].title
18 | - if @item[:event].subtitle
19 | %h2
20 | = h @item[:event].subtitle
21 |
22 | %p.persons
23 | %span.fa{class: @item[:event].persons_icon}
24 | - @item[:event].persons.to_enum.with_index(1).each do |speaker, index|
25 | - if (@item[:event].persons.count - 1) == index
26 | %a{href: '/search/?q=' + h(speaker)}= speaker
27 | and
28 | - elsif @item[:event].persons.count == index
29 | %a{href: '/search/?q=' + h(speaker)}= speaker
30 | - else
31 | = succeed "," do
32 | %a{href: '/search/?q=' + h(speaker)}= speaker
33 |
34 |
35 | %ul.nav.nav-tabs.nav-justified{role: 'tablist', 'data-tabs' => 'tabs'}
36 | - if @item[:video_recordings].present?
37 | %li
38 | %a{href: '#video', role: 'tab', 'data-toggle' => 'tab'} Video
39 | - if @item[:audio_recordings].present?
40 | %li
41 | %a{href: '#audio', role: 'tab', 'data-toggle' => 'tab'} Audio
42 | %li
43 | %a{href: @item[:event].download_url, 'data-target' => '#download', role: 'tab', 'data-toggle' => 'tab'} Download
44 |
45 | %li
46 | %a{href: '#embedshare', role: 'tab', 'data-toggle' => 'tab'} Share
47 |
48 | .tab-content
49 | - if @item[:video_recordings].present?
50 | #video.tab-pane.active
51 | = render '/partials/videoplayer'
52 |
53 | - if @item[:audio_recordings].present?
54 | #audio.tab-pane
55 | %audio.audio{controls: 'controls'}
56 | - @item[:audio_recordings].each do |recording|
57 | %source{type: recording.display_mime_type, src: h(recording.url)}
58 |
59 | #download.tab-pane
60 | = render '/partials/download'
61 |
62 | #embedshare.tab-pane
63 | = render '/partials/embedshare'
64 |
65 | %ul.metadata
66 | %li
67 | %span.fa.fa-clock-o
68 | = recording_length(@item[:event].recordings.downloaded)
69 | %li
70 | %span.fa.fa-calendar-o
71 | = date(@item[:event])
72 | %li
73 | %span.fa.fa-eye
74 | = @item[:event].view_count
75 | %li
76 | %span.fa.fa-external-link
77 | %a{href: @item[:event].link}
78 | = parse_url_host(@item[:event])
79 |
80 | - if @item[:event].description.present?
81 | %h3 About
82 | %p.description
83 | = @item[:event].description
84 |
85 | - if @item[:event].tags.present?
86 | %h3 Tags
87 | - @item[:event].tags.each do |tag|
88 | = link_for(tag)
89 |
90 |
91 |
92 | = render '/partials/footer'
93 |
94 | %script(src='/assets/js/bootstrap.min.js')
95 | %script(src='/assets/js/mediaelement-and-player.min.js')
96 |
97 | = render '/partials/videoplayer_js'
98 |
99 | :javascript
100 |
101 | $('audio').mediaelementplayer();
102 |
103 | // activate tab via hash and default to video
104 | function setTabToHash() {
105 | var hash = window.location.hash.split('&')[0]; // split video-stamp of the hash
106 | var activeTab = $('.nav-tabs a[href=' + hash + '], .nav-tabs a[data-target=' + hash + ']')
107 | .tab('show');
108 |
109 | if (activeTab.length === 0) {
110 | var hash = '#video';
111 | if(window.history && window.history.replaceState) {
112 | // set new hash without adding an entry into the browser history
113 | window.history.replaceState(null, "", hash);
114 | }
115 | else {
116 | // classic fallback
117 | window.location.hash = hash;
118 | }
119 | }
120 | }
121 | setTabToHash();
122 |
123 | // change hash on tab change
124 | $('.nav-tabs a').on('shown.bs.tab', function (e) {
125 | window.location.hash = e.target.hash;
126 | });
127 |
128 | // adjust tabs when hash changes
129 | window.onhashchange = setTabToHash;
130 |
--------------------------------------------------------------------------------
/layouts/browse-show-tags.haml:
--------------------------------------------------------------------------------
1 | !!! 5
2 | %html{lang: 'en'}
3 | %head
4 | = render '/partials/header'
5 | %title
6 | C3TV -
7 | = h @item[:title]
8 | %body
9 | = render '/partials/navbar'
10 |
11 | %div.container-fluid
12 | %h1 Events for tag "#{@item[:title]}"
13 |
14 | .event-previews-tags
15 | - if @item[:events].present?
16 | - @item[:events].each do |event|
17 | - recordings = event.recordings.downloaded
18 | .event-preview
19 | %a{href: h(event.url)}
20 | %img.conference-logo{src: h(event.conference.logo_url), alt: h(event.conference.title)}
21 | .caption
22 | %ul.metadata
23 | %li
24 | %span.fa.fa-clock-o
25 | = recording_length(recordings)
26 | %li
27 | %span.fa.fa-calendar-o
28 | = date(event)
29 | %li.persons
30 | %span.fa{class: event.persons_icon}
31 | = event.linked_persons_text
32 | %h3
33 | %a{href: h(event.url)}
34 | = h event.title
35 |
36 | = yield
37 |
38 | = render '/partials/footer'
39 |
--------------------------------------------------------------------------------
/layouts/default.haml:
--------------------------------------------------------------------------------
1 | !!! 5
2 | %html{lang: 'en'}
3 | %head
4 | = render '/partials/header'
5 | %title
6 | C3TV -
7 | = @item[:title]
8 | %script(src="/assets/js/jquery.min.js")
9 | %script(src="/assets/js/bootstrap.min.js")
10 | %script(src="/assets/js/purl.min.js")
11 | %script(src="/assets/js/search.js")
12 | %body
13 | = render '/partials/navbar'
14 | = yield
15 |
16 | = render '/partials/footer'
17 |
18 |
--------------------------------------------------------------------------------
/layouts/partials/download.haml:
--------------------------------------------------------------------------------
1 | - if @item[:video_recordings].present?
2 | %h3 Video
3 | %ul
4 | - video_download_sources(@item[:video_recordings]).each do |recording|
5 | %li
6 | %span.label.filetype= recording.filetype
7 | %a{href: recording.url, download: true}
8 | %span.fa.fa-download
9 | http download
10 | %a{href: recording.torrent_url, download: true}
11 | %span.fa.fa-download
12 | torrent
13 |
14 | - if @item[:audio_recordings].present?
15 | %h3 Audio
16 | %ul
17 | - @item[:audio_recordings].each do |recording|
18 | %li
19 | .label.filetype= recording.filetype
20 | %a{href: recording.url, download: true}
21 | %span.fa.fa-download
22 | http download
23 | %a{href: recording.torrent_url, download: true}
24 | %span.fa.fa-download
25 | torrent
26 |
27 | %h3 Parent directory
28 | %a{href: @item[:conference].recordings_url}
29 | %span.fa.fa-folder-open
30 | = @item[:conference].recordings_path
31 |
--------------------------------------------------------------------------------
/layouts/partials/embedshare.haml:
--------------------------------------------------------------------------------
1 | %h3 Embed into your website
2 | %input{:type => "text", :size=> "85", :readonly => true, :id => "txtShareFrame",
3 | :value => ""}
4 | %h3 Share with others
5 | %ul
6 | %li
7 | %a{:id => "btnTweet", :href => twitter_url(@item[:title], page_url(@item)) }
8 | %i{:class => "fa-twitter"}
9 | via Twitter
10 | %li
11 | %a{:id => "btnFacebook",
12 | :href => facebook_url(@item[:title], page_url(@item)) }
13 | %i{:class => "fa-facebook-square"}
14 | via Facebook
15 | %li
16 | %a{:id => "btnGPlus", :href => googleplus_url(@item[:title], page_url(@item)) }
17 | %i{:class => "fa-google-plus"}
18 | via Google+
19 | %li
20 | %a{:id => "btnAppNet", :href => appnet_url(@item[:title], page_url(@item)) }
21 | %i{:class => "fa-adn"}
22 | via App.net
23 | %li
24 | %a{:id => "btnMail", :href => mail_url(@item[:title], page_url(@item)) }
25 | %i{:class => "fa-envelope"}
26 | by Mail
27 | :javascript
28 | $(function() {
29 | $('#btnTweet, #btnFacebook, #btnGPlus, #btnAppNet').on('click', function (e) {
30 | window.open(this.href, 'share it', 'width=550,height=420,resizable=yes');
31 | e.preventDefault();
32 | });
33 | $("#txtShareFrame").on('focus', function() {
34 | $(this).select();
35 | });
36 | });
37 |
38 |
--------------------------------------------------------------------------------
/layouts/partials/folder-feeds.haml:
--------------------------------------------------------------------------------
1 | - path = '../' if @item.identifier.match %r[(?:date|duration|name)/$]
2 | - @item[:conference].mime_types do |mime_type, mime_type_name|
3 | %link{rel: "alternate", type: "application/rss+xml", title: "Podcast feed #{MimeType.humanized_mime_type(mime_type)} for this folder", href: "#{path}podcast-#{mime_type_name}.xml"}
4 | %link{rel: "alternate", type: "application/rss+xml", title: "Torrent feed #{MimeType.humanized_mime_type(mime_type)} for this folder", href: "#{path}broadcatching-#{mime_type_name}.rss"}
5 |
--------------------------------------------------------------------------------
/layouts/partials/footer.haml:
--------------------------------------------------------------------------------
1 | %footer
2 | by
3 | %a.inverted{href: '//ccc.de'} Chaos Computer Club e.V
4 |
--------------------------------------------------------------------------------
/layouts/partials/header.haml:
--------------------------------------------------------------------------------
1 | %meta{'http-equiv' => 'X-UA-Compatible', content: 'IE=edge'}
2 | %meta{'http-equiv' => 'Content-Type', content: 'text/html; charset=UTF-8'}
3 | %meta{name: "robots", content: "index,follow"}
4 | %meta{name: 'author', content: Settings.header['author']}
5 | %meta{name: 'description', content: Settings.header['description']}
6 | %meta{name: "keywords", content: keywords}
7 | %meta{name: "google-site-verification", content: Settings.header['google']}
8 | %meta{name: 'viewport', content: 'width=device-width, initial-scale=1.0'}
9 |
10 | %link{rel: "apple-touch-icon", sizes: "57x57", href: "/apple-touch-icon-57x57.png"}
11 | %link{rel: "apple-touch-icon", sizes: "72x72", href: "/apple-touch-icon-72x72.png"}
12 | %link{rel: "apple-touch-icon", sizes: "60x60", href: "/apple-touch-icon-60x60.png"}
13 | %link{rel: "apple-touch-icon", sizes: "76x76", href: "/apple-touch-icon-76x76.png"}
14 | %link{rel: "icon", type: "image/png", href: "/favicon-96x96.png", sizes: "96x96"}
15 | %link{rel: "icon", type: "image/png", href: "/favicon-16x16.png", sizes: "16x16"}
16 | %link{rel: "icon", type: "image/png", href: "/favicon-32x32.png", sizes: "32x32"}
17 |
18 | %link{href: '/assets/css/font-awesome.min.css', rel: 'stylesheet'}
19 | %link{href: '/assets/css/styles.css', rel: 'stylesheet'}
20 | %link{rel: 'alternate', type: 'application/atom+xml', title: 'C3TV - News', href: '/news.atom'}
21 | %link{rel: "alternate", type: "application/rss+xml", title: "C3TV - RSS, last 100", href: "/updates.rdf"}
22 | %link{rel: "alternate", type: "application/rss+xml", title: "C3TV - Podcast feed of the last two years", href: "/podcast.xml"}
23 | %link{rel: "alternate", type: "application/rss+xml", title: "C3TV - Podcast archive feed", href: "/podcast-archive.xml"}
24 |
--------------------------------------------------------------------------------
/layouts/partials/navbar.haml:
--------------------------------------------------------------------------------
1 | %div.navbar.navbar-default.navbar-fixed-top(role="navigation")
2 | %div.container-fluid
3 | %div.navbar-header
4 | %a.navbar-brand(href='/index.html')
5 | %img{src: '/images/tv.png', alt: 'ccc-tv logo, a tv displaying a play icon'}
6 | ccc-tv
7 |
8 | .nav.navbar-form.navbar-right.button-wrapper
9 | - if @item[:conference] and Recording.recorded_at(@item[:conference]).count > 0
10 | - path = '../' if @item.identifier.match %r[(?:date|duration|name)/$]
11 | %a.form-control.btn.btn-default{href: "#{path}podcast-#{@item[:conference].preferred_mime_type}.xml"}
12 | %span.fa.fa-rss
13 | %a.form-control.btn.btn-default{href: "#{path}broadcatching-#{@item[:conference].preferred_mime_type}.rss"}
14 | %span.fa.fa-magnet
15 | - else
16 | %a.form-control.btn.btn-default{href: '/podcast.xml'}
17 | %span.fa.fa-rss
18 |
19 | %span.fa.fa-magnet
20 | %a.form-control.btn.btn-default{href: '/about.html'}
21 | %span.fa.fa-info
22 | %form.navbar-form.navbar-right{role: 'search', action: '/search/', method: 'get', id: 'media-search'}
23 | %div.form-group.input-group
24 | %input.form-control{type: 'search', size: '17', name: 'q', placeholder: 'Search…'}
25 | %span.input-group-btn
26 | %button.btn.btn-default{type: 'submit'}
27 | %span.fa.fa-search
28 |
29 | - event_page_or_folder(@item) do |trail|
30 | #breadcrumb
31 | %ol.container-fluid.breadcrumb
32 | - trail[1..-2].each_with_index do |item, i|
33 | %li
34 | %a.inverted{href: item.identifier}
35 | = item.identifier.sub(trail[i].identifier, '').gsub('/','').gsub(/[\-_]/,' ')
36 | %li.active
37 | - if @item[:sorting] == '' || @item[:sorting] == nil
38 | - if trail[-1][:event].present?
39 | = trail[-1][:title]
40 | - else
41 | = trail[-1].identifier.sub(trail[-2].identifier, '').gsub('/','')
42 | - else
43 | = "sorted by #{trail[-1].identifier.sub(trail[-2].identifier, '').gsub('/','')}"
44 |
--------------------------------------------------------------------------------
/layouts/partials/promoted.haml:
--------------------------------------------------------------------------------
1 | %div.promoted
2 | .titlebar
3 | .carousel.slide{'data-ride' => 'carousel'}
4 | .carousel-inner
5 | - Event.promoted(10).each_with_index do |event, i|
6 | %a.item{href: h(event.url), 'class' => (i == 0 and 'active')}
7 | %img{src: h(event.thumb_url), alt: event.title, title: event.title}
8 | .carousel-caption
9 | %h2.title= event.title
10 | - if event.subtitle.present?
11 | %h3.subtitle= event.subtitle
12 |
--------------------------------------------------------------------------------
/layouts/partials/search.haml:
--------------------------------------------------------------------------------
1 | .search.container-fluid{"data-titletpl" => '"#" - Search - media.ccc.de'}
2 | %form.initial{role: 'search'}
3 | %h1{"data-resulttpl" => 'Search for "#"'}
4 | = @title
5 |
6 | %input.form-control.text{type: 'search', size: '17', name: 'q', placeholder: 'Search…'}
7 | %span.input-group-btn
8 | %button.btn.btn-default.submit{type: 'submit'}
9 | %span.fa.fa-search
10 |
11 | %section.results.initial
12 | .statistics
13 | Results
14 | %span.start #
15 |
16 | to
17 | %span.end #
18 |
19 | of
20 | %span.total #
21 |
22 | Results (in
23 | %span.runtime #
24 | ms)
25 |
26 | %ol.event-previews-search
27 | %li.no-results
28 | Unfortunately your search did not return any results. Maybe you can find what you're looking for by
29 | %a{href: '/tags/'} browsing the tags
30 | or
31 | %a{href: '/browse/'} by category.
32 | %li.template
33 | .event-preview
34 | %a.conference-url{href: '#event.url'}
35 | %img.conference-logo{src: '#event.conference.logo_url', alt: '#event.conference.title'}
36 | .caption
37 | %ul.metadata
38 | %li.recording_length
39 | %span.fa.fa-clock-o
40 | %span.t= 'recording_length(event.recordings.downloaded)'
41 | %li.date
42 | %span.fa.fa-calendar-o
43 | %span.t= 'date(event)'
44 | %li.persons
45 | %span.persons.fa
46 | %span.t= 'event.persons_text'
47 | %h3
48 | %a.conference-url{href: '#event.url'}
49 | %span.number
50 | %span.t= 'event.title'
51 |
52 | %ul.paging
53 | %li.prev
54 | %a.fa.fa-chevron-left{:href => ""}
55 | %li.prefix M
56 |
57 | %li.page.template
58 | %a{:href => "#"}
59 | .letter e
60 | .number 1
61 |
62 | %li.postfix dia.ccc.de
63 | %li.next
64 | %a.fa.fa-chevron-right{:href => ""}
65 |
--------------------------------------------------------------------------------
/layouts/partials/videoplayer.haml:
--------------------------------------------------------------------------------
1 | %video.video{controls: 'controls', poster: h(@item[:event].poster_url), width: aspect_ratio_width, height: aspect_ratio_height}
2 | - video_tag_sources(@item[:video_recordings]).each do |recording|
3 | %source{type: recording.display_mime_type, src: h(recording.url)}
4 | %object{ width: aspect_ratio_width, height: aspect_ratio_height, type: 'application/x-shockwave-flash', data: '/assets/flashmediaelement.swf' }
5 | %param{ name: 'allowFullScreen', value: 'true' }
6 | %param{ name: 'flashvars', value: "controls=true&file=#{flash(@item[:video_recordings])}" }
7 |
--------------------------------------------------------------------------------
/layouts/partials/videoplayer_js.haml:
--------------------------------------------------------------------------------
1 | :javascript
2 | var stamp = window.location.hash.split('&t=')[1];
3 | $('video').mediaelementplayer({
4 | usePluginFullScreen: true,
5 |
6 | pluginPath: '/assets/',
7 | enableAutosize: true,
8 | success: function (mediaElement) {
9 | mediaElement.addEventListener('canplay', function () {
10 | if(stamp) {
11 | mediaElement.setCurrentTime(stamp);
12 | stamp = null;
13 | }
14 | });
15 | mediaElement.addEventListener('playing', function () {
16 | $.post("//api.media.ccc.de/public/recordings/count", {event_id: #{@item[:event].id},src: mediaElement.src});
17 | }, false);
18 | mediaElement.addEventListener('pause', function() {
19 | var hash = '#video&t='+Math.round(mediaElement.currentTime);;
20 | if(window.history && window.history.replaceState) {
21 | // set new hash without adding an entry into the browser history
22 | window.history.replaceState(null, "", hash);
23 | }
24 | else {
25 | // classic fallback
26 | window.location.hash = hash;
27 | }
28 | }, false);
29 | }
30 | });
31 |
--------------------------------------------------------------------------------
/lib/data_source.rb:
--------------------------------------------------------------------------------
1 | require 'active_record'
2 | require 'active_support/all'
3 |
4 | class MediaBackendDataSource < Nanoc3::DataSource
5 |
6 | identifier :media_backend
7 |
8 | def up
9 | ActiveRecord::Base.establish_connection(@site.config[:database])
10 | @cache = ActiveSupport::Cache.lookup_store(:file_store, 'tmp/cache')
11 | end
12 |
13 | def items
14 | item_builder = ItemBuilder.new
15 | build_conference_items(item_builder)
16 | item_builder.items
17 | end
18 |
19 | private
20 |
21 | def build_conference_items(item_builder)
22 | tag_pages = TagPages.new
23 | location_pages = LocationPages.new
24 | feed_builder = FeedBuilder.new(item_builder)
25 |
26 | Conference.order("created_at desc").all.each do |conference|
27 | # event pages
28 | events = conference.events.select { |event| event.recordings.downloaded.any? }
29 |
30 | # conference folders
31 | item_builder.create_conference_item(conference, events)
32 | location_pages.add(conference.webgen_location)
33 |
34 | events.each do |event|
35 | date = cached_date(event)
36 | if ENV['FAST_NANOC']
37 | item_builder.create_event_item(event) if date.nil? || event.updated_at > date
38 | else
39 | item_builder.create_event_item(event)
40 | end
41 | end
42 |
43 | tag_pages.add(events)
44 | feed_builder.add(conference, events)
45 | end
46 |
47 | raise "duplicate location in conferences" if location_pages.duplicate?
48 |
49 | # build tag pages
50 | tag_pages.apply(item_builder)
51 |
52 | # build connecting folder items
53 | location_pages.apply(item_builder)
54 |
55 | feed_builder.apply
56 | end
57 |
58 | # @return nil if item was just added to the cache, otherwise the cached date
59 | def cached_date(event)
60 | key = ['event', event.guid]
61 | date = @cache.read(key)
62 | @cache.write(key, event.updated_at)
63 | date
64 | end
65 |
66 | end
67 |
--------------------------------------------------------------------------------
/lib/data_source/feed_builder.rb:
--------------------------------------------------------------------------------
1 | class FeedBuilder
2 |
3 | def initialize(item_builder)
4 | @item_builder = item_builder
5 | @cache = ActiveSupport::Cache.lookup_store(:file_store, 'tmp/cache')
6 | end
7 |
8 | def add(conference, events)
9 | conference.mime_types do |mime_type, mime_type_name|
10 | name = "podcast-#{mime_type_name}"
11 | xml = @cache.fetch(cache_key(name, conference, events)) do
12 | Feeds::PodcastGenerator.generate events: events, query: :by_mime_type, config: {
13 | mime_type: mime_type,
14 | title: "#{conference.title} (#{mime_type_name})",
15 | channel_summary: "This feed contains all events from #{conference.acronym} as #{mime_type_name}"
16 | }
17 | end
18 | @item_builder.create_folder_feed_item(conference, identifier: name, content: xml)
19 | end
20 |
21 | # broadcatching
22 | conference.mime_types do |mime_type, mime_type_name|
23 | name = "broadcatching-#{mime_type_name}"
24 | xml = @cache.fetch(cache_key(name, conference, events)) do
25 | Feeds::BroadcatchingGenerator.generate events: events, query: :by_mime_type, config: {
26 | mime_type: mime_type,
27 | title: "#{conference.title} (#{mime_type_name})",
28 | channel_summary: "This feed contains all torrents for #{mime_type_name} from #{conference.acronym}"
29 | }
30 | end
31 | @item_builder.create_folder_feed_item(conference, identifier: name, content: xml, extension: 'rss')
32 | end
33 | end
34 |
35 | def apply
36 | # rss 1.0 last 100 feed
37 | events = Event.recent(100)
38 | xml = @cache.fetch(cache_key(:events100, events)) do
39 | Feeds::RDFGenerator.generate events: events, config: {
40 | title: 'last 100 events feed',
41 | channel_summary: "This feed the most recent 100 events"
42 | }
43 | end
44 | @item_builder.create_feed_item(xml, 'updates.rdf')
45 |
46 | # podcast_recent
47 | events = Event.newer(Time.now.ago(2.years))
48 | xml = @cache.fetch(cache_key(:events_two_years, events)) do
49 | Feeds::PodcastGenerator.generate events: events, query: :preferred_recording, config: {
50 | title: 'recent events feed',
51 | channel_summary: "This feed contains events from the last two years"
52 | }
53 | end
54 | @item_builder.create_feed_item(xml, 'podcast.xml')
55 |
56 | # podcast_archive
57 | events = Event.older(Time.now.ago(2.years))
58 | xml = @cache.fetch(cache_key(:events_older, events)) do
59 | Feeds::PodcastGenerator.generate events: events, query: :preferred_recording, config: {
60 | title: 'archive feed',
61 | channel_summary: "This feed contains events older than two years"
62 | }
63 | end
64 | @item_builder.create_feed_item(xml, 'podcast-archive.xml')
65 |
66 | # news atom feed
67 | news = News.all
68 | atom_feed = @cache.fetch(cache_key(:news, news)) do
69 | Feeds::NewsFeedGenerator.generate(news, options: {
70 | author: Settings.feeds['channel_owner'],
71 | about: 'http://media.ccc.de/',
72 | title: 'CCC TV - NEWS',
73 | feed_url: 'http://media.ccc.de/news.atom',
74 | icon: 'http://media.ccc.de/favicons.ico',
75 | logo: 'http://media.ccc.de/images/tv.png'
76 | })
77 | end
78 | @item_builder.create_feed_item(atom_feed, 'news.atom')
79 | end
80 |
81 | private
82 |
83 | def cache_key(identifier, *models)
84 | identifier.to_s + '_' + Digest::SHA1.hexdigest([models.flatten.map { |m| m.updated_at.to_i }].join(';'))
85 | end
86 |
87 | end
88 |
--------------------------------------------------------------------------------
/lib/data_source/item_builder.rb:
--------------------------------------------------------------------------------
1 | class ItemBuilder
2 |
3 | def initialize
4 | @items = []
5 | end
6 | attr_reader :items
7 |
8 | def create_conference_item(conference, events)
9 | ['', 'name', 'duration', 'date'].each do |sorting|
10 | @items << Nanoc3::Item.new(
11 | "",
12 | {
13 | title: conference_title(conference), layout: 'browse-show-folder',
14 | conference: conference, events: sorting_events(events, sorting),
15 | sorting: sorting
16 | },
17 | get_path([conference.webgen_location, "#{sorting}"]),
18 | binary: false
19 | )
20 | end
21 | end
22 |
23 | def create_event_item(event)
24 | description = ""
25 | if event.description.present?
26 | description = event.description
27 | end
28 |
29 | @items << Nanoc3::Item.new(
30 | description,
31 | event_attributes(event).merge(layout: 'browse-show-page'),
32 | get_path(event.conference.webgen_location, event.slug),
33 | binary: false
34 | )
35 |
36 | @items << Nanoc3::Item.new(
37 | description,
38 | event_attributes(event).merge(layout: 'browse-download-page'),
39 | get_path(event.conference.webgen_location, event.slug)+'download/',
40 | binary: false
41 | )
42 |
43 | @items << Nanoc3::Item.new(
44 | description,
45 | event_attributes(event).merge(layout: 'browse-oembed-page'),
46 | get_path(event.conference.webgen_location, event.slug)+'oembed/',
47 | binary: false
48 | )
49 | end
50 |
51 | def create_browse_item(path, childs)
52 | folders = []
53 | childs.each { |child|
54 | if path == '/'
55 | location = child
56 | else
57 | location = File.join(path, child)
58 | end
59 | folder = Folder.new(location: location)
60 | res = Conference.where(webgen_location: location)
61 | folder.conference = res.first unless res.nil? or res.empty?
62 | folders << folder
63 | }
64 |
65 | title = path
66 | if path == "/"
67 | title = "Browse by category"
68 | end
69 |
70 | @items << Nanoc3::Item.new(
71 | "",
72 | {
73 | title: title, layout: 'browse-index',
74 | folders: folders
75 | },
76 | get_path(path),
77 | binary: false
78 | )
79 | end
80 |
81 | def create_tag_item(tag, events)
82 | @items << Nanoc3::Item.new(
83 | "",
84 | {
85 | title: tag, layout: 'browse-show-tags',
86 | tag: true,
87 | events: events
88 | },
89 | get_path('tags', tag),
90 | binary: false
91 | )
92 | end
93 |
94 | def create_feed_item(content, filename)
95 | identifier, extension = filename.split('.')
96 | @items << Nanoc3::Item.new(
97 | content,
98 | {
99 | extension: extension
100 | },
101 | "/#{identifier}/",
102 | binary: false
103 | )
104 | end
105 |
106 | def create_folder_feed_item(conference, content: '', identifier: 'podcast', extension: 'xml')
107 | @items << Nanoc3::Item.new(
108 | content,
109 | {
110 | extension: extension
111 | },
112 | get_path(conference.webgen_location, identifier),
113 | binary: false
114 | )
115 | end
116 |
117 | private
118 |
119 | def event_attributes(event)
120 | {
121 | title: event.title,
122 | tags: event.tags.map { |t| t.strip },
123 | conference: event.conference,
124 | event: event,
125 | video_recordings: event.recordings.downloaded.video,
126 | audio_recordings: event.recordings.downloaded.audio
127 | }
128 | end
129 |
130 | def conference_title(conference)
131 | if conference.title.present?
132 | conference.title
133 | else
134 | conference.acronym
135 | end
136 | end
137 |
138 | def get_path(*parts)
139 | raise "nil found in item path" if parts.any? { |p| p.nil? }
140 | '/browse/' + parts.join('/') + '/'
141 | end
142 |
143 | def sorting_events(events, criteria)
144 | case criteria
145 | when 'name'
146 | events.sort_by{ |e| e.title }
147 | when 'duration'
148 | events.sort_by{ |e| e.recordings.downloaded.first.length.nil? ? 0 : e.recordings.downloaded.first.length }.reverse
149 | when 'date'
150 | events.sort_by{ |e| date(e) }
151 | else
152 | events
153 | end
154 | end
155 |
156 | end
157 |
--------------------------------------------------------------------------------
/lib/data_source/location_pages.rb:
--------------------------------------------------------------------------------
1 | class LocationPages
2 | def initialize
3 | @locations = Hash.new(0)
4 | end
5 |
6 | def add(location)
7 | @locations[location] += 1
8 | end
9 |
10 | def apply(item_builder)
11 | locations = @locations.keys
12 |
13 | root = build_navigation_tree(locations)
14 | walk_navigation_paths(root, []) { |path, childs|
15 | # skip last node
16 | next if locations.include? path
17 | item_builder.create_browse_item(path, childs)
18 | }
19 |
20 | item_builder.create_browse_item('/', root.keys)
21 | end
22 |
23 | def duplicate?
24 | @locations.any? { |k,v| v > 1 }
25 | end
26 |
27 | private
28 |
29 | def build_navigation_tree(locations)
30 | root = {}
31 | locations.each { |location|
32 | node = root
33 | next if location.nil?
34 | location.split(%r{/}).each { |path|
35 | node[path] ||= {}
36 | node = node[path]
37 | }
38 | }
39 | root
40 | end
41 |
42 | def walk_navigation_paths(root, paths, &block)
43 | root.each { |k,leafs|
44 | p = paths.clone
45 | p << k
46 | block.call p.join('/'), leafs.keys
47 | walk_navigation_paths(leafs, p, &block)
48 | }
49 | end
50 |
51 | end
52 |
--------------------------------------------------------------------------------
/lib/data_source/tag_pages.rb:
--------------------------------------------------------------------------------
1 | class TagPages
2 |
3 | def initialize
4 | @tags = Hash.new { |h,k| h[k] = [] }
5 | end
6 | attr_reader :tags
7 |
8 | def add(events)
9 | events.each { |event|
10 | event.tags.each { |tag|
11 | @tags[tag.strip] << event
12 | }
13 | }
14 | end
15 |
16 | def apply(item_builder)
17 | @tags.each { |tag, events|
18 | item_builder.create_tag_item(tag, events)
19 | }
20 | end
21 |
22 | end
23 |
--------------------------------------------------------------------------------
/lib/default.rb:
--------------------------------------------------------------------------------
1 | # All files in the 'lib' directory will be loaded
2 | # before nanoc starts compiling.
3 | require 'ostruct'
4 | require 'json'
5 | require 'haml'
6 | Settings = OpenStruct.new JSON.load(File.open('settings.json')) unless defined? Settings
7 |
--------------------------------------------------------------------------------
/lib/feeds.rb:
--------------------------------------------------------------------------------
1 | module Feeds
2 | load 'lib/feeds/helper.rb'
3 | end
4 |
--------------------------------------------------------------------------------
/lib/feeds/broadcatching.xml.haml:
--------------------------------------------------------------------------------
1 | !!! XML
2 | %rss{version: '2.0', 'xmlns:dc' => 'http://purl.org/dc/elements/1.1/'}
3 | %channel
4 | %title= config['channel_title']
5 | %link= "http#{config['base_url']}"
6 | %description= config['channel_description']
7 | %language= 'en-us, de-de'
8 | %pubDate= Time.now
9 | %lastBuildDate= Time.now
10 | %docs http://blogs.law.harvard.edu/tech/rss
11 | - events.each do |event|
12 | - recording = event.public_send query, mime_type: config[:mime_type]
13 | - if recording
14 | %item
15 | %title= ""
16 | %link= "#{recording.try(:url)}?torrent"
17 | %pubDate= event.date.to_s || event.created_at.to_s
18 | %category= ""
19 | %dc:creator= ""
20 | %guid= recording.try(:url)
21 | %torrent{'xmlns' => 'http://xmlns.ezrss.it/0.1/'}
22 | %infoHash= recording.magnet_info_hash
23 | %magnetURI= ""
24 |
--------------------------------------------------------------------------------
/lib/feeds/broadcatching_generator.rb:
--------------------------------------------------------------------------------
1 | # generate torrent broadcatching feeds for folders
2 | module Feeds
3 | module BroadcatchingGenerator
4 |
5 | def self.generate(events: [], query: nil, config: {})
6 | rss = BroadcatchingGenerator::TorrentRSS.new config
7 | rss.generate events, query
8 | end
9 |
10 | class TorrentRSS
11 | include Feeds::Helper
12 |
13 | def initialize(config)
14 | @config = OpenStruct.new Settings.feeds
15 | merge_config(config)
16 | end
17 | attr_reader :config
18 | attr_writer :config
19 |
20 | def generate(events, query)
21 | template = File.read('lib/feeds/broadcatching.xml.haml')
22 | haml_engine = Haml::Engine.new(template)
23 | output = haml_engine.render(self, events: events, query: query, config: @config)
24 | output
25 | end
26 | end
27 |
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/lib/feeds/helper.rb:
--------------------------------------------------------------------------------
1 | module Feeds
2 |
3 | module Helper
4 |
5 | def merge_config(config)
6 | keep = [ :title, :channel_summary ]
7 | @config.channel_title = [ @config.channel_title, config[:title] ].join(' - ')
8 | @config.channel_summary += config[:channel_summary]
9 |
10 | config.each { |k,v|
11 | next if keep.include? k
12 | @config[k] = v
13 | }
14 | end
15 |
16 | def get_item_title(event)
17 | conference = event.conference
18 | title = ''
19 | if conference.title.present?
20 | title = conference.title
21 | elsif conference.acronym.present?
22 | title = conference.acronym
23 | end
24 | title += ": "
25 | if event.title
26 | title += event.title
27 | else
28 | title += event.slug
29 | end
30 | title
31 | end
32 |
33 | def get_item_description(event)
34 | description = []
35 | description << event.description or event.subtitle
36 |
37 | link = event.link
38 | description << "about this event: #{link}\n" if link
39 |
40 | # file = 'src/browse/bla.page'
41 | url = @config['base_url'] + event.slug + '.html'
42 | description << "event on media: #{url}\n"
43 |
44 | description.join
45 | end
46 |
47 | end
48 |
49 | end
50 |
--------------------------------------------------------------------------------
/lib/feeds/newsfeed_generator.rb:
--------------------------------------------------------------------------------
1 | require 'rss/maker'
2 |
3 | module Feeds
4 | # Atom news feed generator.
5 | module NewsFeedGenerator
6 |
7 | # Generate atom feed for given news.
8 | #
9 | # @example Generate atom feed for given news
10 | # news = News.all
11 | # Feeds::NewsFeedGenerator.generate(news)
12 | # #=> "\n] should be generate to an atom feed
15 | # @param options [Hash] to define feed preferences
16 | # @option opts [String] :author Feed author
17 | # @option opts [String] :about Feed about text
18 | # @option opts [String] :title Feed title
19 | #
20 | # @return [String] atom feed
21 | def self.generate(news, options: {})
22 | atom_feed = build_feed(news, options)
23 | atom_feed.to_s
24 | end
25 |
26 | # Build atom feed object for given news.
27 | #
28 | # @example Generate atom feed object for given news
29 | # news = News.all
30 | # Feeds::NewsFeedGenerator.events(news) #=> #
31 | #
32 | # @param news [Array] to convert to an atom feed
33 | # @param options [Hash] to define feed preferences
34 | # @option opts [String] :author Feed author
35 | # @option opts [String] :about Feed about text
36 | # @option opts [String] :title Feed title
37 | #
38 | # @return [RSS::Atom::Feed] atom feed
39 | def self.build_feed(news, options)
40 | RSS::Maker.make('atom') do |feed|
41 | assign_feed_options(feed, options)
42 |
43 | # Create feed item for every news
44 | news.each do |news|
45 | feed.items.new_item do |item|
46 | assign_item_options(item, news)
47 | end
48 | end
49 |
50 |
51 | end
52 | end
53 |
54 | protected
55 |
56 | # Assign news options to an atom feed item.
57 | #
58 | # @param item [RSS::Maker::Atom::Feed::Items::Item]
59 | # @param news [News] object
60 | def self.assign_item_options(item, news)
61 | item.id = "tag:media.ccc.de,#{news.created_at.strftime('%Y-%m-%d')}:#{news.id}"
62 | item.updated = Time.now.to_s
63 | item.title = news.title
64 | item.content.content = news.body
65 | item.content.type = 'html'
66 | item.published = news.created_at
67 | item.updated = news.updated_at
68 | item.link = 'http://media.ccc.de/'
69 | end
70 |
71 | # Assign options like a title or an author to an atom feed.
72 | #
73 | # @param feed [RSS::Maker::Atom::Feed] atom feed
74 | # @param options [Hash] options
75 | def self.assign_feed_options(feed, options)
76 | feed.channel.author = options[:author]
77 | feed.channel.updated = Time.now.to_s
78 | feed.channel.about = options[:about]
79 | feed.channel.title = options[:title]
80 | feed.channel.link = options[:feed_url]
81 | feed.channel.icon = options[:icon]
82 | feed.channel.logo = options[:logo]
83 | # define feed link attributes
84 | feed.channel.links.first.rel = 'self'
85 | feed.channel.links.first.type = 'application/atom+xml'
86 | end
87 | end
88 | end
89 |
--------------------------------------------------------------------------------
/lib/feeds/podcast_generator.rb:
--------------------------------------------------------------------------------
1 | # generate iTunes store feed
2 | module Feeds
3 | module PodcastGenerator
4 |
5 | def self.generate(events: [], query: nil, config: {})
6 | rss = PodcastGenerator::UpdatesITS.new config
7 | rss.generate events, query
8 | end
9 |
10 | class UpdatesITS
11 | require 'rss/2.0'
12 | require 'rss/itunes'
13 | require 'rss/maker'
14 | require 'rss/content'
15 | include Feeds::Helper
16 |
17 | def initialize(config)
18 | @config = OpenStruct.new Settings.feeds
19 | merge_config(config)
20 | end
21 | attr_reader :config
22 | attr_writer :config
23 |
24 | def generate(events, query)
25 | rss = RSS::Maker.make("2.0") do |maker|
26 |
27 | create_channel(maker)
28 |
29 |
30 | events.each do |event|
31 |
32 | recording = event.public_send query, mime_type: @config[:mime_type]
33 | next if recording.nil?
34 |
35 | fill_item(maker.items.new_item, event, recording)
36 | end
37 |
38 | end
39 | rss.to_s
40 | end
41 |
42 | private
43 |
44 | def create_channel(maker)
45 | maker.channel.title = @config['channel_title']
46 | maker.channel.link = @config['base_url']
47 | maker.channel.description = @config['channel_description']
48 | maker.channel.copyright = "mostly cc-by-nc"
49 | maker.channel.language = "en-us, de-de"
50 | maker.channel.lastBuildDate = Time.now
51 |
52 | # see http://www.apple.com/itunes/podcasts/specs.html#category
53 | #category = maker.channel.itunes_categories.new_category
54 | #category.text = "Technology"
55 | #category.new_category.text = "Technology"
56 | maker.channel.itunes_categories.new_category.text = "Technology"
57 |
58 | # TODO png/jpg?
59 | maker.image.url = @config['logo_image']
60 | maker.image.title = @config['channel_title']
61 | maker.channel.itunes_author = @config['channel_owner']
62 | maker.channel.itunes_owner.itunes_name = @config['channel_owner']
63 | maker.channel.itunes_owner.itunes_email='media@koeln.ccc.de'
64 | maker.channel.itunes_keywords = @config['channel_keywords']
65 | maker.channel.itunes_subtitle = @config['channel_subtitle']
66 | maker.channel.itunes_summary = @config['channel_summary']
67 |
68 | maker.channel.itunes_image = @config['logo_image']
69 | maker.channel.itunes_explicit = "No"
70 | end
71 |
72 | def fill_item(item, event, recording)
73 | item.title = get_item_title(event)
74 | item.link = recording.url
75 | item.itunes_keywords = event.try(:tags).join(',')
76 | item.guid.content = recording.url
77 | item.guid.isPermaLink = true
78 | item.dc_identifier = event.guid
79 |
80 | # FIXME decode, redcloth, whatever
81 | description = get_item_description(event)
82 | item.description = description
83 | item.itunes_duration = Time.at(recording.length).utc.strftime "%H:%M:%S"
84 | item.itunes_summary = description
85 | item.itunes_explicit = "No"
86 | item.pubDate = event.created_at.to_s
87 |
88 | item.itunes_subtitle = event.subtitle if event.subtitle.present?
89 | item.itunes_author = event.persons.join(', ') if event.persons.present?
90 | item.pubDate = event.date.to_s if event.date.present?
91 |
92 | item.enclosure.url = recording.url
93 | item.enclosure.length = recording.size || 0
94 | item.enclosure.type = recording.display_mime_type
95 | end
96 | end
97 |
98 | end
99 | end
100 |
--------------------------------------------------------------------------------
/lib/feeds/rdf_generator.rb:
--------------------------------------------------------------------------------
1 | # generate rssfeed from svn log
2 | module Feeds
3 | module RDFGenerator
4 |
5 | def self.generate(events: [], config: {})
6 | rss = RDFGenerator::UpdatesRDF.new config
7 | rss.generate events
8 | end
9 |
10 | class UpdatesRDF
11 | require 'rss'
12 | require 'rss/1.0'
13 | require 'rss/maker'
14 | require 'rss/content'
15 | include Feeds::Helper
16 |
17 | def initialize(config)
18 | @config = OpenStruct.new Settings.feeds
19 | merge_config(config)
20 | end
21 | attr_reader :config
22 | attr_writer :config
23 |
24 | def generate(events)
25 | rss = RSS::Maker.make("1.0") do |maker|
26 |
27 | create_channel(maker)
28 |
29 | events.each do |event|
30 |
31 | recording = event.preferred_recording
32 | next if recording.nil?
33 |
34 | fill_item(maker.items.new_item, event, recording)
35 | end
36 |
37 | end
38 | rss.to_s
39 | end
40 |
41 | private
42 |
43 | def create_channel(maker)
44 | maker.channel.title = @config.channel_title
45 | maker.channel.about = File.join(@config.base_url, 'updates.rdf')
46 | maker.channel.link = @config.base_url
47 | maker.channel.description = @config.channel_description
48 | maker.image.title = @config.channel_title
49 | maker.image.url = @config.logo_image
50 | #maker.items.do_sort = true
51 | end
52 |
53 | def fill_item(item, event, recording)
54 | item.link = recording.url
55 | item.title = get_item_title(event)
56 | item.description = get_item_description(event)
57 |
58 | item.content_encoded = <
60 | #{item.description}
61 | 
62 | Video:#{recording.filename}
63 |
64 | EOF
65 | item.pubDate = date(event)
66 |
67 | end
68 |
69 | end
70 | end
71 | end
72 |
--------------------------------------------------------------------------------
/lib/helpers.rb:
--------------------------------------------------------------------------------
1 | include Nanoc::Helpers::Rendering
2 | include Nanoc::Helpers::Breadcrumbs
3 | include Nanoc::Helpers::XMLSitemap
4 |
--------------------------------------------------------------------------------
/lib/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 | require 'uri'
3 | require 'nanoc/helpers/html_escape'
4 | include Nanoc::Helpers::HTMLEscape
5 |
6 | def twitter_url(title, url)
7 | "http://twitter.com/home?status="+URI.encode_www_form_component(title+": "+url)
8 | end
9 |
10 | def facebook_url(title, url)
11 | "https://www.facebook.com/sharer/sharer.php?t="+URI.encode_www_form_component(title)+"&u="+URI.encode_www_form_component(url)
12 | end
13 |
14 | def googleplus_url(title, url)
15 | "https://plus.google.com/share?title="+URI.encode_www_form_component(title)+"&url="+URI.encode_www_form_component(url)
16 | end
17 |
18 | def appnet_url(title, url)
19 | "https://alpha.app.net/intent/post?text="+URI.encode_www_form_component(title+": "+url)
20 | end
21 |
22 | def mail_url(title, url)
23 | content = URI.encode_www_form_component(title+': '+url)
24 | subject = URI.encode_www_form_component(title)
25 | URI::MailTo.build(['', [['Subject', subject], ['Body', content]]]).to_s
26 | end
27 |
28 | def oembed_url(identifier)
29 | Settings.oembedURL + identifier[0..-2] + '.html'
30 | end
31 |
32 | def oembed_page_url(identifier)
33 | id = identifier+"oembed/"
34 | oembed = @items.find { |i| i.identifier == id }
35 | Settings.baseURL + oembed.path
36 | end
37 |
38 | def page_url(identifier)
39 | Settings.baseURL + identifier.path
40 | end
41 |
42 | def event_page_or_folder(item)
43 | return if @item.identifier.include? '/tags/'
44 | trail = breadcrumbs_trail
45 | return unless trail.count > 2 or @item.identifier.include? '/browse/'
46 | trail = trail[0..-2] if @item.identifier.include? '/download/'
47 | yield trail
48 | end
49 |
50 | def video_download_sources(recordings)
51 | skip = %w[vnd.voc/mp4-web vnd.voc/webm-web]
52 | recordings.reject { |r| skip.include? r.mime_type }
53 | end
54 |
55 | def video_tag_sources(recordings, order=MimeType::WEB_PREFERRED_VIDEO)
56 | scores = {}
57 | recordings.select { |r| order.include? r.mime_type }.each { |r|
58 | pos = order.index r.mime_type
59 | fail r.mime_type unless pos
60 | if scores.has_key? r.display_mime_type
61 | scores[r.display_mime_type] = pos unless scores[r.display_mime_type] < pos
62 | else
63 | scores[r.display_mime_type] = pos
64 | end
65 | }
66 | scores.map { |_, pos|
67 | mime_type = order[pos]
68 | recordings.detect { |r| r.mime_type == mime_type }
69 | }
70 | end
71 |
72 | def keywords
73 | if @item[:event] and @item[:event].tags
74 | [ @item[:event].tags, Settings.header['keywords']].join(', ')
75 | else
76 | Settings.header['keywords']
77 | end
78 | end
79 |
80 | def recording_length(recordings)
81 | return unless recordings.present?
82 | recording = recordings.select { |r| r.length.present? }.first
83 | recording_length_minutes(recording) unless recording.nil?
84 | end
85 |
86 | def recording_length_minutes(recording)
87 | if recording.length > 0
88 | "#{recording.length / 60} min"
89 | end
90 | end
91 |
92 | def flash(recordings)
93 | url = recordings.select { |recording| recording.display_mime_type == 'video/mp4' }.first.try(:url)
94 | if url.present?
95 | h(url)
96 | elsif recordings.present?
97 | h(recordings.first.url)
98 | else
99 | # :(
100 | ''
101 | end
102 | end
103 |
104 | def aspect_ratio_width(high=true)
105 | conference = @item[:conference]
106 | case conference.aspect_ratio
107 | when /16:9/
108 | high ? '640' : '188'
109 | when /4:3/
110 | high ? '400' : '120'
111 | else
112 | end
113 | end
114 |
115 | def aspect_ratio_height(high=true)
116 | conference = @item[:conference]
117 | case conference.aspect_ratio
118 | when /16:9/
119 | high ? '360' : '144'
120 | when /4:3/
121 | high ? '300' : '90'
122 | else
123 | end
124 | end
125 |
126 | def date(event)
127 | date = event.release_date || event.date
128 | date.strftime("%Y-%m-%d") if date
129 | end
130 |
131 | def datetime(event)
132 | date = event.release_date || event.date
133 | date.strftime("%Y-%m-%d %H:%M") if date
134 | end
135 |
136 | def parse_url_host(urlish)
137 | begin
138 | URI.parse(@item[:event].link).host()
139 | rescue URI::InvalidURIError
140 | return ""
141 | end
142 | end
143 | end
144 |
--------------------------------------------------------------------------------
/lib/helpers/logo_filter_helper.rb:
--------------------------------------------------------------------------------
1 | class Thumbnailize < Nanoc::Filter
2 |
3 | identifier :logo
4 | type :binary
5 |
6 | def run(filename, params={})
7 | system(
8 | 'gm',
9 | 'convert',
10 | filename,
11 | '-resize', 'x100',
12 | '-background', 'white',
13 | '-gravity', 'Center',
14 | '-extent', 'x100',
15 | '+profile', '*',
16 | output_filename
17 | )
18 | end
19 |
20 | end
--------------------------------------------------------------------------------
/lib/helpers/tagging_helper.rb:
--------------------------------------------------------------------------------
1 | # Set the tags attribute on items to use this helper
2 | module TaggingHelper
3 |
4 | require 'nanoc/helpers/html_escape'
5 | include Nanoc::Helpers::HTMLEscape
6 |
7 | # link to tag page
8 | def link_for(tag, prefix: '/browse/tags/', css: '')
9 | %[#{h tag}]
10 | end
11 |
12 | #
13 | def tag_cloud(prefix: '/browse/tags/')
14 | return if @items.nil?
15 | tags_hash.map { |tag, count|
16 | link_for tag, prefix: prefix, css: css_class_by_size(count)
17 | }
18 | end
19 |
20 | private
21 |
22 | def css_class_by_size(n)
23 | if n < 5
24 | "xtiny"
25 | elsif n < 10
26 | "tiny"
27 | elsif n < 50
28 | "normal"
29 | elsif n < 100
30 | "large"
31 | else
32 | "xlarge"
33 | end
34 | end
35 |
36 | def tags_hash
37 | tags = Hash.new { |h,k| h[k] = [] }
38 | @items.each do |i|
39 | next unless i[:tag]
40 | tags[i[:title]] = i[:events].count
41 | end
42 | tags
43 | end
44 |
45 | end
46 |
--------------------------------------------------------------------------------
/lib/helpers_.rb:
--------------------------------------------------------------------------------
1 | # from nanocs documentation:
2 | # the underscore is necessary to ensure that the file is loaded after the helpers are loaded, which is necessary because files in lib/ are loaded in alphabetical order
3 | include ApplicationHelper
4 | include TaggingHelper
5 |
--------------------------------------------------------------------------------
/lib/magnet_link_provider.rb:
--------------------------------------------------------------------------------
1 | class MagnetLinkProvider
2 | include Singleton
3 |
4 | def fetch(recording)
5 | begin
6 | magnet = download(recording).chomp
7 | [magnet[20..59], magnet.to_s]
8 | rescue OpenURI::HTTPError => ex
9 | STDERR.puts "Failed to download URL #{recording.url} : #{ex.message}"
10 | [nil,nil]
11 | end
12 | end
13 |
14 | private
15 |
16 | def cache
17 | @cache ||= ActiveSupport::Cache.lookup_store(:file_store, 'tmp/cache_magnets')
18 | end
19 |
20 | def download(recording)
21 | cache.fetch(cache_key(:magnet, recording)) do
22 | #`curl #{recording.url}?magnet`
23 | download_uri("#{recording.url}?magnet")
24 | end
25 | end
26 |
27 | def cache_key(identifier, model)
28 | identifier.to_s + '_' + Digest::SHA1.hexdigest([model.id, model.updated_at.to_i].join(';'))
29 | end
30 |
31 | def download_uri(url)
32 | content = ''
33 | url = url.gsub(/ /, '%20')
34 | uri = URI(URI.escape(url))
35 |
36 | if uri.scheme.nil?
37 | uri = URI('https:' + uri.to_s)
38 | end
39 |
40 | if uri.scheme == 'file'
41 | File.open(uri.path, 'r:UTF-8') { |f| content = f.read }
42 | else
43 | content = open(url).read
44 | end
45 | content.to_s
46 | end
47 | end
48 |
49 |
--------------------------------------------------------------------------------
/lib/mime_type.rb:
--------------------------------------------------------------------------------
1 | class MimeType
2 | HTML5 = %w[audio/ogg audio/mpeg audio/opus video/mp4 video/ogg video/webm vnd.voc/h264-lq vnd.voc/h264-sd vnd.voc/webm-hd vnd.voc/h264-hd]
3 | PREFERRED_VIDEO = %w[vnd.voc/h264-hd vnd.voc/h264-lq video/mp4 vnd.voc/h264-sd vnd.voc/webm-hd video/webm video/ogg]
4 | WEB_PREFERRED_VIDEO = %w[vnd.voc/mp4-web vnd.voc/webm-web vnd.voc/h264-hd vnd.voc/h264-lq video/mp4 vnd.voc/h264-sd vnd.voc/webm-hd video/webm video/ogg]
5 |
6 | class << self
7 |
8 | def mime_type_slug(mime_type)
9 | humanized_mime_type(mime_type).to_param.downcase
10 | end
11 |
12 | def display_mime_type(mime_type)
13 | case mime_type
14 | when 'vnd.voc/h264-lq'
15 | 'video/mp4'
16 | when 'vnd.voc/h264-sd'
17 | 'video/mp4'
18 | when 'vnd.voc/h264-hd'
19 | 'video/mp4'
20 | when 'vnd.voc/mp4-web'
21 | 'video/mp4'
22 | when 'vnd.voc/webm-hd'
23 | 'video/webm'
24 | when 'vnd.voc/webm-web'
25 | 'video/webm'
26 | else
27 | mime_type
28 | end
29 | end
30 |
31 | def hd?(mime_type)
32 | case mime_type
33 | when 'vnd.voc/h264-lq'
34 | false
35 | when 'vnd.voc/h264-sd'
36 | false
37 | when 'vnd.voc/h264-hd'
38 | true
39 | when 'vnd.voc/webm-hd'
40 | true
41 | else
42 | nil
43 | end
44 | end
45 |
46 | def humanized_mime_type(mime_type)
47 | case mime_type
48 | when 'vnd.voc/h264-lq'
49 | 'MP4 (LQ)'
50 | when 'vnd.voc/h264-sd'
51 | 'MP4 (SD)'
52 | when 'vnd.voc/h264-hd'
53 | 'MP4 (HD)'
54 | when 'vnd.voc/webm-hd'
55 | 'WEBM (HD)'
56 | when 'vnd.voc/webm-web'
57 | 'WEBM (html5)'
58 | when 'vnd.voc/mp4-web'
59 | 'MP4 (html5)'
60 | when 'audio/mpeg'
61 | 'MP3'
62 | else
63 | mime_type.split('/')[1]
64 | end
65 | end
66 |
67 | end
68 |
69 | end
70 |
--------------------------------------------------------------------------------
/lib/models/conference.rb:
--------------------------------------------------------------------------------
1 | class Conference < ActiveRecord::Base
2 | has_many :events
3 |
4 | def mime_types
5 | Recording.recorded_at(self).pluck(:mime_type).uniq.map { |mime_type|
6 | yield mime_type, MimeType.mime_type_slug(mime_type)
7 | }
8 | end
9 |
10 | def preferred_mime_type
11 | available = Recording.recorded_at(self).pluck(:mime_type).uniq
12 | MimeType::PREFERRED_VIDEO.each { |mime_type|
13 | return MimeType.mime_type_slug(mime_type) if available.include? mime_type
14 | }
15 | return MimeType.mime_type_slug(available.first)
16 | end
17 |
18 | def recordings_url
19 | File.join Settings.cdnURL, self.recordings_path
20 | end
21 |
22 | def logo_url
23 | if self.logo
24 | File.join '/images/logos', self.images_path, File.basename(self.logo, File.extname(self.logo))+'.png'
25 | else
26 | File.join '/images/logos/unknown.png'
27 | end
28 | end
29 |
30 | end
31 |
--------------------------------------------------------------------------------
/lib/models/event.rb:
--------------------------------------------------------------------------------
1 | class Event < ActiveRecord::Base
2 | belongs_to :conference
3 | has_many :recordings
4 |
5 | serialize :persons, Array
6 | serialize :tags, Array
7 |
8 | scope :promoted, ->(n) { where(promoted: true).order('updated_at desc').limit(n) }
9 | scope :recent, ->(n) { order('release_date desc').limit(n) }
10 | scope :newer, ->(date) { where("release_date > ?", date).order('release_date desc') }
11 | scope :older, ->(date) { where("release_date < ?", date).order('release_date desc') }
12 |
13 | scope :recorded_at, ->(conference) {
14 | joins(:recordings, :conference)
15 | .where(conferences: { id: conference })
16 | .where(recordings: { state: 'downloaded', mime_type: MimeType::HTML5 })
17 | .group(:"events.id")
18 | }
19 |
20 | def title
21 | read_attribute(:title).strip
22 | end
23 |
24 | def url
25 | "/browse/#{self.conference.webgen_location}/#{self.slug}.html"
26 | end
27 |
28 | def download_url
29 | "/browse/#{self.conference.webgen_location}/#{self.slug}/download.html#download"
30 | end
31 |
32 | def poster_url
33 | File.join(Settings.staticURL, 'media', self.conference.images_path, self.poster_filename) if self.poster_filename
34 | end
35 |
36 | def thumb_url
37 | File.join Settings.staticURL, 'media', self.conference.images_path, self.thumb_filename
38 | end
39 |
40 | def tags
41 | read_attribute(:tags).compact.collect { |x| x.strip }
42 | end
43 |
44 | def persons_text
45 | if self.persons.length == 0
46 | 'n/a'
47 | elsif self.persons.length == 1
48 | self.persons[0]
49 | else
50 | persons = self.persons[0..-3] + [self.persons[-2..-1].join(' and ')]
51 | persons.join(', ')
52 | end
53 | end
54 |
55 | def linked_persons_text
56 | if self.persons.length == 0
57 | 'n/a'
58 | elsif self.persons.length == 1
59 | linkify_persons(self.persons)[0]
60 | else
61 | persons = linkify_persons(self.persons)
62 | persons = persons[0..-3] + [persons[-2..-1].join(' and ')]
63 | persons.join(', ')
64 | end
65 | end
66 |
67 | def linkify_persons(persons)
68 | persons.map { |person| ''+CGI.escapeHTML(person)+'' }
69 | end
70 |
71 | def persons_icon
72 | if self.persons.length <= 1
73 | 'fa-user'
74 | else
75 | 'fa-group'
76 | end
77 | end
78 |
79 | def preferred_recording(order: MimeType::PREFERRED_VIDEO, mime_type: nil)
80 | recordings = recordings_by_mime_type
81 | return if recordings.empty?
82 | order.each { |mt|
83 | return recordings[mt] if recordings.has_key?(mt)
84 | }
85 | recordings.first[1]
86 | end
87 |
88 | def by_mime_type(order: nil, mime_type: 'video/mp4')
89 | recordings.downloaded.by_mime_type(mime_type).first
90 | end
91 |
92 | private
93 |
94 | def recordings_by_mime_type
95 | Hash[recordings.downloaded.map { |r| [r.mime_type, r] }]
96 | end
97 |
98 | end
99 |
--------------------------------------------------------------------------------
/lib/models/folder.rb:
--------------------------------------------------------------------------------
1 | class Folder
2 | def initialize(location:nil)
3 | @location = location
4 | end
5 | attr_accessor :location, :conference
6 |
7 | def name
8 | File.basename @location
9 | end
10 |
11 | def url
12 | "/browse/#{@location}/index.html"
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/lib/models/news.rb:
--------------------------------------------------------------------------------
1 | class News < ActiveRecord::Base
2 | scope :recent, ->(n) { order('date desc').limit(n) }
3 |
4 | def date_formatted
5 | self.date.strftime("%d.%m.%Y")
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/lib/models/recording.rb:
--------------------------------------------------------------------------------
1 | class Recording < ActiveRecord::Base
2 | belongs_to :event
3 |
4 | scope :downloaded, -> { where(state: 'downloaded') }
5 | scope :by_mime_type, ->(mime_type) { where(mime_type: mime_type) }
6 | scope :audio, -> { where(mime_type: %w[audio/ogg audio/mpeg audio/opus]) }
7 | scope :video, -> { where(mime_type: %w[vnd.voc/mp4-web vnd.voc/webm-web video/mp4 vnd.voc/h264-lq vnd.voc/h264-hd vnd.voc/h264-sd vnd.voc/webm-hd video/ogg video/webm]) }
8 | scope :recorded_at, ->(conference) { joins(event: :conference).where(events: {'conference_id' => conference} ) }
9 |
10 | def url
11 | File.join self.event.conference.recordings_url, self.folder || '', self.filename
12 | end
13 |
14 | def torrent_url
15 | url + '.torrent'
16 | end
17 |
18 | def display_mime_type
19 | MimeType.display_mime_type(mime_type)
20 | end
21 |
22 | def filetype
23 | MimeType.humanized_mime_type(mime_type)
24 | end
25 |
26 | def magnet_uri
27 | _, link = torrent_magnet_data
28 | link
29 | end
30 |
31 | def magnet_info_hash
32 | hash, _ = torrent_magnet_data
33 | hash
34 | end
35 |
36 | private
37 |
38 | def torrent_magnet_data
39 | MagnetLinkProvider.instance.fetch self
40 | end
41 |
42 | end
43 |
--------------------------------------------------------------------------------
/nanoc.yaml.example:
--------------------------------------------------------------------------------
1 | # A list of file extensions that nanoc will consider to be textual rather than
2 | # binary. If an item with an extension not in this list is found, the file
3 | # will be considered as binary.
4 | text_extensions: [ 'coffee', 'css', 'erb', 'haml', 'handlebars', 'hb', 'htm', 'html', 'js', 'less', 'markdown', 'md', 'ms', 'mustache', 'php', 'rb', 'sass', 'scss', 'txt', 'xhtml', 'xml', 'atom' ]
5 |
6 | # The path to the directory where all generated files will be written to. This
7 | # can be an absolute path starting with a slash, but it can also be path
8 | # relative to the site directory.
9 | output_dir: output
10 | encoding: utf-8
11 |
12 | # sitemap.xml
13 | base_url: http://media.ccc.de
14 |
15 | # A list of index filenames, i.e. names of files that will be served by a web
16 | # server when a directory is requested. Usually, index files are named
17 | # “index.html”, but depending on the web server, this may be something else,
18 | # such as “default.htm”. This list is used by nanoc to generate pretty URLs.
19 | index_filenames: [ 'index.html' ]
20 |
21 | # Whether or not to generate a diff of the compiled content when compiling a
22 | # site. The diff will contain the differences between the compiled content
23 | # before and after the last site compilation.
24 | enable_output_diff: false
25 |
26 | prune:
27 | # Whether to automatically remove files not managed by nanoc from the output
28 | # directory. For safety reasons, this is turned off by default.
29 | auto_prune: false
30 |
31 | # Which files and directories you want to exclude from pruning. If you version
32 | # your output directory, you should probably exclude VCS directories such as
33 | # .git, .svn etc.
34 | exclude: [ '.git', '.hg', '.svn', 'CVS' ]
35 |
36 | # The data sources where nanoc loads its data from. This is an array of
37 | # hashes; each array element represents a single data source. By default,
38 | # there is only a single data source that reads data from the “content/” and
39 | # “layout/” directories in the site directory.
40 | data_sources:
41 | -
42 | # The type is the identifier of the data source. By default, this will be
43 | # `filesystem_unified`.
44 | type: filesystem_unified
45 |
46 | # The path where items should be mounted (comparable to mount points in
47 | # Unix-like systems). This is “/” by default, meaning that items will have
48 | # “/” prefixed to their identifiers. If the items root were “/en/”
49 | # instead, an item at content/about.html would have an identifier of
50 | # “/en/about/” instead of just “/about/”.
51 | items_root: /
52 |
53 | # The path where layouts should be mounted. The layouts root behaves the
54 | # same as the items root, but applies to layouts rather than items.
55 | layouts_root: /
56 |
57 | # nanoc defaults to environment input encoding
58 | # for the sake of windows users use utf-8 *everywhere*
59 | encoding: utf-8
60 |
61 | # Whether to allow periods in identifiers. When turned off, everything
62 | # past the first period is considered to be the extension, and when
63 | # turned on, only the characters past the last period are considered to
64 | # be the extension. For example, a file named “content/about.html.erb”
65 | # will have the identifier “/about/” when turned off, but when turned on
66 | # it will become “/about.html/” instead.
67 | allow_periods_in_identifiers: false
68 | -
69 | type: static
70 | items_root: /assets/
71 | -
72 | type: static
73 | items_root: /images/
74 | prefix: images
75 | -
76 | type: media_backend
77 |
78 | database:
79 | adapter: 'sqlite3'
80 | database: 'development.sqlite3'
81 | encoding: utf8
82 |
--------------------------------------------------------------------------------
/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "baseURL": "//media.ccc.de",
3 | "oembedURL": "//api.media.ccc.de/public/oembed?url=http://media.ccc.de",
4 | "cdnURL": "https://cdn.media.ccc.de",
5 | "staticURL": "//static.media.ccc.de",
6 | "header": {
7 | "description": "Video Streaming Portal des Chaos Computer Clubs",
8 | "author": "CCC",
9 | "google": "2ozQIr-cQ-sJzGeJT_iTkS147fs3c5R3I1i6Dk_YLwA",
10 | "keywords": "Chaos Computer Club, Video, Media, Streaming, TV, Hacker"
11 | },
12 | "feeds": {
13 | "base_url": "//media.ccc.de/",
14 | "channel_title": "Chaos Computer Club",
15 | "channel_subtitle": "A wide variety of video material distributed by the CCC. All content is taken from cdn.media.ccc.de and media.ccc.de",
16 | "channel_description": "Der Chaos Computer Club ist die größte europäische Hackervereinigung, und seit über 25 Jahren Vermittler im Spannungsfeld technischer und sozialer Entwicklungen.",
17 | "channel_summary": "A wide variety of video material distributed by the Chaos Computer Club.",
18 | "channel_owner": "CCC media team",
19 | "logo_image": "//media.ccc.de/images/miro-banner.png",
20 | "channel_keywords": "CCC Congress Hacking Security Netzpolitik"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/static/css/bigplay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/static/css/bigplay.png
--------------------------------------------------------------------------------
/static/css/bigplay.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/css/controls.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/static/css/controls.png
--------------------------------------------------------------------------------
/static/css/controls.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/css/font-awesome.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Font Awesome 4.1.0 by @davegandy - http://fontawesome.io - @fontawesome
3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
4 | */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.1.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.1.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff?v=4.1.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.1.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.1.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:spin 2s infinite linear;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;animation:spin 2s infinite linear}@-moz-keyframes spin{0%{-moz-transform:rotate(0deg)}100%{-moz-transform:rotate(359deg)}}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg)}}@-o-keyframes spin{0%{-o-transform:rotate(0deg)}100%{-o-transform:rotate(359deg)}}@keyframes spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-moz-transform:scale(-1, 1);-ms-transform:scale(-1, 1);-o-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-moz-transform:scale(1, -1);-ms-transform:scale(1, -1);-o-transform:scale(1, -1);transform:scale(1, -1)}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-square:before,.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}
--------------------------------------------------------------------------------
/static/css/loading.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/static/css/loading.gif
--------------------------------------------------------------------------------
/static/css/mediaelementplayer.min.css:
--------------------------------------------------------------------------------
1 | .mejs-container{position:relative;background:#000;font-family:Helvetica,Arial;text-align:left;vertical-align:top;text-indent:0;}.me-plugin{position:absolute;}.mejs-embed,.mejs-embed body{width:100%;height:100%;margin:0;padding:0;background:#000;overflow:hidden;}.mejs-fullscreen{overflow:hidden!important;}.mejs-container-fullscreen{position:fixed;left:0;top:0;right:0;bottom:0;overflow:hidden;z-index:1000;}.mejs-container-fullscreen .mejs-mediaelement,.mejs-container-fullscreen video{width:100%;height:100%;}.mejs-clear{clear:both;}.mejs-background{position:absolute;top:0;left:0;}.mejs-mediaelement{position:absolute;top:0;left:0;width:100%;height:100%;}.mejs-poster{position:absolute;top:0;left:0;background-size:contain;background-position:50% 50%;background-repeat:no-repeat;}:root .mejs-poster img{display:none;}.mejs-poster img{border:0;padding:0;border:0;}.mejs-overlay{position:absolute;top:0;left:0;}.mejs-overlay-play{cursor:pointer;}.mejs-overlay-button{position:absolute;top:50%;left:50%;width:100px;height:100px;margin:-50px 0 0 -50px;background:url(bigplay.svg) no-repeat;}.no-svg .mejs-overlay-button{background-image:url(bigplay.png);}.mejs-overlay:hover .mejs-overlay-button{background-position:0 -100px;}.mejs-overlay-loading{position:absolute;top:50%;left:50%;width:80px;height:80px;margin:-40px 0 0 -40px;background:#333;background:url(background.png);background:rgba(0,0,0,0.9);background:-webkit-gradient(linear,0% 0,0% 100%,from(rgba(50,50,50,0.9)),to(rgba(0,0,0,0.9)));background:-webkit-linear-gradient(top,rgba(50,50,50,0.9),rgba(0,0,0,0.9));background:-moz-linear-gradient(top,rgba(50,50,50,0.9),rgba(0,0,0,0.9));background:-o-linear-gradient(top,rgba(50,50,50,0.9),rgba(0,0,0,0.9));background:-ms-linear-gradient(top,rgba(50,50,50,0.9),rgba(0,0,0,0.9));background:linear-gradient(rgba(50,50,50,0.9),rgba(0,0,0,0.9));}.mejs-overlay-loading span{display:block;width:80px;height:80px;background:transparent url(loading.gif) 50% 50% no-repeat;}.mejs-container .mejs-controls{position:absolute;list-style-type:none;margin:0;padding:0;bottom:0;left:0;background:url(background.png);background:rgba(0,0,0,0.7);background:-webkit-gradient(linear,0% 0,0% 100%,from(rgba(50,50,50,0.7)),to(rgba(0,0,0,0.7)));background:-webkit-linear-gradient(top,rgba(50,50,50,0.7),rgba(0,0,0,0.7));background:-moz-linear-gradient(top,rgba(50,50,50,0.7),rgba(0,0,0,0.7));background:-o-linear-gradient(top,rgba(50,50,50,0.7),rgba(0,0,0,0.7));background:-ms-linear-gradient(top,rgba(50,50,50,0.7),rgba(0,0,0,0.7));background:linear-gradient(rgba(50,50,50,0.7),rgba(0,0,0,0.7));height:30px;width:100%;}.mejs-container .mejs-controls div{list-style-type:none;background-image:none;display:block;float:left;margin:0;padding:0;width:26px;height:26px;font-size:11px;line-height:11px;font-family:Helvetica,Arial;border:0;}.mejs-controls .mejs-button button{cursor:pointer;display:block;font-size:0;line-height:0;text-decoration:none;margin:7px 5px;padding:0;position:absolute;height:16px;width:16px;border:0;background:transparent url(controls.svg) no-repeat;}.no-svg .mejs-controls .mejs-button button{background-image:url(controls.png);}.mejs-controls .mejs-button button:focus{outline:dotted 1px #999;}.mejs-container .mejs-controls .mejs-time{color:#fff;display:block;height:17px;width:auto;padding:8px 3px 0 3px;overflow:hidden;text-align:center;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;}.mejs-container .mejs-controls .mejs-time span{color:#fff;font-size:11px;line-height:12px;display:block;float:left;margin:1px 2px 0 0;width:auto;}.mejs-controls .mejs-play button{background-position:0 0;}.mejs-controls .mejs-pause button{background-position:0 -16px;}.mejs-controls .mejs-stop button{background-position:-112px 0;}.mejs-controls div.mejs-time-rail{direction:ltr;width:200px;padding-top:5px;}.mejs-controls .mejs-time-rail span{display:block;position:absolute;width:180px;height:10px;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px;cursor:pointer;}.mejs-controls .mejs-time-rail .mejs-time-total{margin:5px;background:#333;background:rgba(50,50,50,0.8);background:-webkit-gradient(linear,0% 0,0% 100%,from(rgba(30,30,30,0.8)),to(rgba(60,60,60,0.8)));background:-webkit-linear-gradient(top,rgba(30,30,30,0.8),rgba(60,60,60,0.8));background:-moz-linear-gradient(top,rgba(30,30,30,0.8),rgba(60,60,60,0.8));background:-o-linear-gradient(top,rgba(30,30,30,0.8),rgba(60,60,60,0.8));background:-ms-linear-gradient(top,rgba(30,30,30,0.8),rgba(60,60,60,0.8));background:linear-gradient(rgba(30,30,30,0.8),rgba(60,60,60,0.8));}.mejs-controls .mejs-time-rail .mejs-time-buffering{width:100%;background-image:-o-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-ms-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);-webkit-background-size:15px 15px;-moz-background-size:15px 15px;-o-background-size:15px 15px;background-size:15px 15px;-webkit-animation:buffering-stripes 2s linear infinite;-moz-animation:buffering-stripes 2s linear infinite;-ms-animation:buffering-stripes 2s linear infinite;-o-animation:buffering-stripes 2s linear infinite;animation:buffering-stripes 2s linear infinite;}@-webkit-keyframes buffering-stripes{from{background-position:0 0;}to{background-position:30px 0;}}@-moz-keyframes buffering-stripes{from{background-position:0 0;}to{background-position:30px 0;}}@-ms-keyframes buffering-stripes{from{background-position:0 0;}to{background-position:30px 0;}}@-o-keyframes buffering-stripes{from{background-position:0 0;}to{background-position:30px 0;}}@keyframes buffering-stripes{from{background-position:0 0;}to{background-position:30px 0;}}.mejs-controls .mejs-time-rail .mejs-time-loaded{background:#3caac8;background:rgba(60,170,200,0.8);background:-webkit-gradient(linear,0% 0,0% 100%,from(rgba(44,124,145,0.8)),to(rgba(78,183,212,0.8)));background:-webkit-linear-gradient(top,rgba(44,124,145,0.8),rgba(78,183,212,0.8));background:-moz-linear-gradient(top,rgba(44,124,145,0.8),rgba(78,183,212,0.8));background:-o-linear-gradient(top,rgba(44,124,145,0.8),rgba(78,183,212,0.8));background:-ms-linear-gradient(top,rgba(44,124,145,0.8),rgba(78,183,212,0.8));background:linear-gradient(rgba(44,124,145,0.8),rgba(78,183,212,0.8));width:0;}.mejs-controls .mejs-time-rail .mejs-time-current{background:#fff;background:rgba(255,255,255,0.8);background:-webkit-gradient(linear,0% 0,0% 100%,from(rgba(255,255,255,0.9)),to(rgba(200,200,200,0.8)));background:-webkit-linear-gradient(top,rgba(255,255,255,0.9),rgba(200,200,200,0.8));background:-moz-linear-gradient(top,rgba(255,255,255,0.9),rgba(200,200,200,0.8));background:-o-linear-gradient(top,rgba(255,255,255,0.9),rgba(200,200,200,0.8));background:-ms-linear-gradient(top,rgba(255,255,255,0.9),rgba(200,200,200,0.8));background:linear-gradient(rgba(255,255,255,0.9),rgba(200,200,200,0.8));width:0;}.mejs-controls .mejs-time-rail .mejs-time-handle{display:none;position:absolute;margin:0;width:10px;background:#fff;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;cursor:pointer;border:solid 2px #333;top:-2px;text-align:center;}.mejs-controls .mejs-time-rail .mejs-time-float{position:absolute;display:none;background:#eee;width:36px;height:17px;border:solid 1px #333;top:-26px;margin-left:-18px;text-align:center;color:#111;}.mejs-controls .mejs-time-rail .mejs-time-float-current{margin:2px;width:30px;display:block;text-align:center;left:0;}.mejs-controls .mejs-time-rail .mejs-time-float-corner{position:absolute;display:block;width:0;height:0;line-height:0;border:solid 5px #eee;border-color:#eee transparent transparent transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;top:15px;left:13px;}.mejs-long-video .mejs-controls .mejs-time-rail .mejs-time-float{width:48px;}.mejs-long-video .mejs-controls .mejs-time-rail .mejs-time-float-current{width:44px;}.mejs-long-video .mejs-controls .mejs-time-rail .mejs-time-float-corner{left:18px;}.mejs-controls .mejs-fullscreen-button button{background-position:-32px 0;}.mejs-controls .mejs-unfullscreen button{background-position:-32px -16px;}.mejs-controls .mejs-mute button{background-position:-16px -16px;}.mejs-controls .mejs-unmute button{background-position:-16px 0;}.mejs-controls .mejs-volume-button{position:relative;}.mejs-controls .mejs-volume-button .mejs-volume-slider{display:none;height:115px;width:25px;background:url(background.png);background:rgba(50,50,50,0.7);-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;top:-115px;left:0;z-index:1;position:absolute;margin:0;}.mejs-controls .mejs-volume-button:hover{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}.mejs-controls .mejs-volume-button .mejs-volume-slider .mejs-volume-total{position:absolute;left:11px;top:8px;width:2px;height:100px;background:#ddd;background:rgba(255,255,255,0.5);margin:0;}.mejs-controls .mejs-volume-button .mejs-volume-slider .mejs-volume-current{position:absolute;left:11px;top:8px;width:2px;height:100px;background:#ddd;background:rgba(255,255,255,0.9);margin:0;}.mejs-controls .mejs-volume-button .mejs-volume-slider .mejs-volume-handle{position:absolute;left:4px;top:-3px;width:16px;height:6px;background:#ddd;background:rgba(255,255,255,0.9);cursor:N-resize;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;margin:0;}.mejs-controls div.mejs-horizontal-volume-slider{height:26px;width:60px;position:relative;}.mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-total{position:absolute;left:0;top:11px;width:50px;height:8px;margin:0;padding:0;font-size:1px;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px;background:#333;background:rgba(50,50,50,0.8);background:-webkit-gradient(linear,0% 0,0% 100%,from(rgba(30,30,30,0.8)),to(rgba(60,60,60,0.8)));background:-webkit-linear-gradient(top,rgba(30,30,30,0.8),rgba(60,60,60,0.8));background:-moz-linear-gradient(top,rgba(30,30,30,0.8),rgba(60,60,60,0.8));background:-o-linear-gradient(top,rgba(30,30,30,0.8),rgba(60,60,60,0.8));background:-ms-linear-gradient(top,rgba(30,30,30,0.8),rgba(60,60,60,0.8));background:linear-gradient(rgba(30,30,30,0.8),rgba(60,60,60,0.8));}.mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-current{position:absolute;left:0;top:11px;width:50px;height:8px;margin:0;padding:0;font-size:1px;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px;background:#fff;background:rgba(255,255,255,0.8);background:-webkit-gradient(linear,0% 0,0% 100%,from(rgba(255,255,255,0.9)),to(rgba(200,200,200,0.8)));background:-webkit-linear-gradient(top,rgba(255,255,255,0.9),rgba(200,200,200,0.8));background:-moz-linear-gradient(top,rgba(255,255,255,0.9),rgba(200,200,200,0.8));background:-o-linear-gradient(top,rgba(255,255,255,0.9),rgba(200,200,200,0.8));background:-ms-linear-gradient(top,rgba(255,255,255,0.9),rgba(200,200,200,0.8));background:linear-gradient(rgba(255,255,255,0.9),rgba(200,200,200,0.8));}.mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-handle{display:none;}.mejs-controls .mejs-captions-button{position:relative;}.mejs-controls .mejs-captions-button button{background-position:-48px 0;}.mejs-controls .mejs-captions-button .mejs-captions-selector{visibility:hidden;position:absolute;bottom:26px;right:-10px;width:130px;height:100px;background:url(background.png);background:rgba(50,50,50,0.7);border:solid 1px transparent;padding:10px;overflow:hidden;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}.mejs-controls .mejs-captions-button .mejs-captions-selector ul{margin:0;padding:0;display:block;list-style-type:none!important;overflow:hidden;}.mejs-controls .mejs-captions-button .mejs-captions-selector ul li{margin:0 0 6px 0;padding:0;list-style-type:none!important;display:block;color:#fff;overflow:hidden;}.mejs-controls .mejs-captions-button .mejs-captions-selector ul li input{clear:both;float:left;margin:3px 3px 0 5px;}.mejs-controls .mejs-captions-button .mejs-captions-selector ul li label{width:100px;float:left;padding:4px 0 0 0;line-height:15px;font-family:helvetica,arial;font-size:10px;}.mejs-controls .mejs-captions-button .mejs-captions-translations{font-size:10px;margin:0 0 5px 0;}.mejs-chapters{position:absolute;top:0;left:0;-xborder-right:solid 1px #fff;width:10000px;z-index:1;}.mejs-chapters .mejs-chapter{position:absolute;float:left;background:#222;background:rgba(0,0,0,0.7);background:-webkit-gradient(linear,0% 0,0% 100%,from(rgba(50,50,50,0.7)),to(rgba(0,0,0,0.7)));background:-webkit-linear-gradient(top,rgba(50,50,50,0.7),rgba(0,0,0,0.7));background:-moz-linear-gradient(top,rgba(50,50,50,0.7),rgba(0,0,0,0.7));background:-o-linear-gradient(top,rgba(50,50,50,0.7),rgba(0,0,0,0.7));background:-ms-linear-gradient(top,rgba(50,50,50,0.7),rgba(0,0,0,0.7));background:linear-gradient(rgba(50,50,50,0.7),rgba(0,0,0,0.7));filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0,startColorstr=#323232,endColorstr=#000000);overflow:hidden;border:0;}.mejs-chapters .mejs-chapter .mejs-chapter-block{font-size:11px;color:#fff;padding:5px;display:block;border-right:solid 1px #333;border-bottom:solid 1px #333;cursor:pointer;}.mejs-chapters .mejs-chapter .mejs-chapter-block-last{border-right:none;}.mejs-chapters .mejs-chapter .mejs-chapter-block:hover{background:#666;background:rgba(102,102,102,0.7);background:-webkit-gradient(linear,0% 0,0% 100%,from(rgba(102,102,102,0.7)),to(rgba(50,50,50,0.6)));background:-webkit-linear-gradient(top,rgba(102,102,102,0.7),rgba(50,50,50,0.6));background:-moz-linear-gradient(top,rgba(102,102,102,0.7),rgba(50,50,50,0.6));background:-o-linear-gradient(top,rgba(102,102,102,0.7),rgba(50,50,50,0.6));background:-ms-linear-gradient(top,rgba(102,102,102,0.7),rgba(50,50,50,0.6));background:linear-gradient(rgba(102,102,102,0.7),rgba(50,50,50,0.6));filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0,startColorstr=#666666,endColorstr=#323232);}.mejs-chapters .mejs-chapter .mejs-chapter-block .ch-title{font-size:12px;font-weight:bold;display:block;white-space:nowrap;text-overflow:ellipsis;margin:0 0 3px 0;line-height:12px;}.mejs-chapters .mejs-chapter .mejs-chapter-block .ch-timespan{font-size:12px;line-height:12px;margin:3px 0 4px 0;display:block;white-space:nowrap;text-overflow:ellipsis;}.mejs-captions-layer{position:absolute;bottom:0;left:0;text-align:center;line-height:22px;font-size:12px;color:#fff;}.mejs-captions-layer a{color:#fff;text-decoration:underline;}.mejs-captions-layer[lang=ar]{font-size:20px;font-weight:normal;}.mejs-captions-position{position:absolute;width:100%;bottom:15px;left:0;}.mejs-captions-position-hover{bottom:45px;}.mejs-captions-text{padding:3px 5px;background:url(background.png);background:rgba(20,20,20,0.8);}.me-cannotplay a{color:#fff;font-weight:bold;}.me-cannotplay span{padding:15px;display:block;}.mejs-controls .mejs-loop-off button{background-position:-64px -16px;}.mejs-controls .mejs-loop-on button{background-position:-64px 0;}.mejs-controls .mejs-backlight-off button{background-position:-80px -16px;}.mejs-controls .mejs-backlight-on button{background-position:-80px 0;}.mejs-controls .mejs-picturecontrols-button{background-position:-96px 0;}.mejs-contextmenu{position:absolute;width:150px;padding:10px;border-radius:4px;top:0;left:0;background:#fff;border:solid 1px #999;z-index:1001;}.mejs-contextmenu .mejs-contextmenu-separator{height:1px;font-size:0;margin:5px 6px;background:#333;}.mejs-contextmenu .mejs-contextmenu-item{font-family:Helvetica,Arial;font-size:12px;padding:4px 6px;cursor:pointer;color:#333;}.mejs-contextmenu .mejs-contextmenu-item:hover{background:#2C7C91;color:#fff;}.mejs-controls .mejs-sourcechooser-button{position:relative;}.mejs-controls .mejs-sourcechooser-button button{background-position:-128px 0;}.mejs-controls .mejs-sourcechooser-button .mejs-sourcechooser-selector{visibility:hidden;position:absolute;bottom:26px;right:-10px;width:130px;height:100px;background:url(background.png);background:rgba(50,50,50,0.7);border:solid 1px transparent;padding:10px;overflow:hidden;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}.mejs-controls .mejs-sourcechooser-button .mejs-sourcechooser-selector ul{margin:0;padding:0;display:block;list-style-type:none!important;overflow:hidden;}.mejs-controls .mejs-sourcechooser-button .mejs-sourcechooser-selector ul li{margin:0 0 6px 0;padding:0;list-style-type:none!important;display:block;color:#fff;overflow:hidden;}.mejs-controls .mejs-sourcechooser-button .mejs-sourcechooser-selector ul li input{clear:both;float:left;margin:3px 3px 0 5px;}.mejs-controls .mejs-sourcechooser-button .mejs-sourcechooser-selector ul li label{width:100px;float:left;padding:4px 0 0 0;line-height:15px;font-family:helvetica,arial;font-size:10px;}.mejs-postroll-layer{position:absolute;bottom:0;left:0;width:100%;height:100%;background:url(background.png);background:rgba(50,50,50,0.7);z-index:1000;overflow:hidden;}.mejs-postroll-layer-content{width:100%;height:100%;}.mejs-postroll-close{position:absolute;right:0;top:0;background:url(background.png);background:rgba(50,50,50,0.7);color:#fff;padding:4px;z-index:100;cursor:pointer;}
--------------------------------------------------------------------------------
/static/flashmediaelement.swf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/static/flashmediaelement.swf
--------------------------------------------------------------------------------
/static/fonts/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/static/fonts/FontAwesome.otf
--------------------------------------------------------------------------------
/static/fonts/bootstrap/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/static/fonts/bootstrap/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/static/fonts/bootstrap/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/static/fonts/bootstrap/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/static/fonts/bootstrap/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/static/fonts/bootstrap/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/static/fonts/estre.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/static/fonts/estre.eot
--------------------------------------------------------------------------------
/static/fonts/estre.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/static/fonts/estre.otf
--------------------------------------------------------------------------------
/static/fonts/estre.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/static/fonts/estre.ttf
--------------------------------------------------------------------------------
/static/fonts/estre.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/static/fonts/estre.woff
--------------------------------------------------------------------------------
/static/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/static/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/static/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/static/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/static/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/static/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/static/js/purl.min.js:
--------------------------------------------------------------------------------
1 | /*! purl v2.3.1 | MIT */
2 | (function(factory){if(typeof define==="function"&&define.amd){define(factory)}else{window.purl=factory()}})(function(){var tag2attr={a:"href",img:"src",form:"action",base:"href",script:"src",iframe:"src",link:"href"},key=["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","fragment"],aliases={anchor:"fragment"},parser={strict:/^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,loose:/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/},isint=/^[0-9]+$/;function parseUri(url,strictMode){var str=decodeURI(url),res=parser[strictMode||false?"strict":"loose"].exec(str),uri={attr:{},param:{},seg:{}},i=14;while(i--){uri.attr[key[i]]=res[i]||""}uri.param["query"]=parseString(uri.attr["query"]);uri.param["fragment"]=parseString(uri.attr["fragment"]);uri.seg["path"]=uri.attr.path.replace(/^\/+|\/+$/g,"").split("/");uri.seg["fragment"]=uri.attr.fragment.replace(/^\/+|\/+$/g,"").split("/");uri.attr["base"]=uri.attr.host?(uri.attr.protocol?uri.attr.protocol+"://"+uri.attr.host:uri.attr.host)+(uri.attr.port?":"+uri.attr.port:""):"";return uri}function getAttrName(elm){var tn=elm.tagName;if(typeof tn!=="undefined")return tag2attr[tn.toLowerCase()];return tn}function promote(parent,key){if(parent[key].length===0)return parent[key]={};var t={};for(var i in parent[key])t[i]=parent[key][i];parent[key]=t;return t}function parse(parts,parent,key,val){var part=parts.shift();if(!part){if(isArray(parent[key])){parent[key].push(val)}else if("object"==typeof parent[key]){parent[key]=val}else if("undefined"==typeof parent[key]){parent[key]=val}else{parent[key]=[parent[key],val]}}else{var obj=parent[key]=parent[key]||[];if("]"==part){if(isArray(obj)){if(""!==val)obj.push(val)}else if("object"==typeof obj){obj[keys(obj).length]=val}else{obj=parent[key]=[parent[key],val]}}else if(~part.indexOf("]")){part=part.substr(0,part.length-1);if(!isint.test(part)&&isArray(obj))obj=promote(parent,key);parse(parts,obj,part,val)}else{if(!isint.test(part)&&isArray(obj))obj=promote(parent,key);parse(parts,obj,part,val)}}}function merge(parent,key,val){if(~key.indexOf("]")){var parts=key.split("[");parse(parts,parent,"base",val)}else{if(!isint.test(key)&&isArray(parent.base)){var t={};for(var k in parent.base)t[k]=parent.base[k];parent.base=t}if(key!==""){set(parent.base,key,val)}}return parent}function parseString(str){return reduce(String(str).split(/&|;/),function(ret,pair){try{pair=decodeURIComponent(pair.replace(/\+/g," "))}catch(e){}var eql=pair.indexOf("="),brace=lastBraceInKey(pair),key=pair.substr(0,brace||eql),val=pair.substr(brace||eql,pair.length);val=val.substr(val.indexOf("=")+1,val.length);if(key===""){key=pair;val=""}return merge(ret,key,val)},{base:{}}).base}function set(obj,key,val){var v=obj[key];if(typeof v==="undefined"){obj[key]=val}else if(isArray(v)){v.push(val)}else{obj[key]=[v,val]}}function lastBraceInKey(str){var len=str.length,brace,c;for(var i=0;i>0,curr=arguments[2];while(i ol > li.template').detach(),
10 | $noresults = $results.find('> ol > li.no-results').detach(),
11 | baseUrl = window.location.protocol+'//'+window.location.host+window.location.pathname,
12 | baseTitleTpl = $search.data('titletpl'),
13 | pageNr = 0,
14 | perPage = 15;
15 |
16 | $search
17 | .find('input.text')
18 | .focus()
19 | .end()
20 | .on('click', '.submit', function(e, triggerOrigin, displayPage) {
21 | e.preventDefault();
22 | var
23 | $submit = $(this),
24 | $form = $submit.closest('form'),
25 | $input = $form.find('input.text'),
26 | term = $input.val(),
27 | lterm = term.toLowerCase(),
28 | title = baseTitleTpl.replace('#', $input.val());
29 |
30 | displayPage = displayPage || 0;
31 |
32 | document.title = title;
33 | if(window.history && window.history.pushState)
34 | window.history.pushState({}, title, '?' + (displayPage > 0 ? 'p='+displayPage+'&' : '') + 'q='+encodeURIComponent(term));
35 |
36 | $input.blur();
37 | $('#media-search input[name=q]').val(term);
38 | $.ajax({
39 | dataType: $.support.cors ? 'json' : 'jsonp',
40 | url: window.location.protocol+'//koeln.media.ccc.de/search/api/term',
41 | type: 'post',
42 | data: {
43 | term: lterm,
44 | displayPage: displayPage,
45 | perPage: perPage
46 | },
47 | success: function(res) {
48 | var
49 | conferenceSearchBase = $template.find('.conference-search').data('titletpl'),
50 | eventSearchBase = $template.find('.event-search').data('titletpl'),
51 | $h1 = $search.find('form h1'),
52 | $list = $results
53 | .find('> ol')
54 | .attr('start', displayPage * perPage + 1)
55 | .find('> li')
56 | .remove()
57 | .end();
58 |
59 | $h1.text($h1.data('resulttpl').replace('#', $input.val()));
60 |
61 | $statistics
62 | .find('.start')
63 | .text(displayPage * perPage + 1)
64 | .end()
65 | .find('.end')
66 | .text(displayPage * perPage + res.hits.hits.length)
67 | .end()
68 | .find('.total')
69 | .text(res.hits.total)
70 | .end()
71 | .find('.runtime')
72 | .text(res.took);
73 |
74 | $paging.toggleClass('visible', res.hits.total > res.hits.hits.length);
75 | if(res.hits.total > res.hits.hits.length) {
76 | $paging.find('li.page').remove();
77 | var
78 | maxpages = res.hits.total / perPage,
79 | npages = Math.min(Math.max(10, displayPage+3), Math.ceil(maxpages));
80 |
81 | for (var i = 0; i < npages; i++) {
82 | $pagingTemplate
83 | .clone()
84 | .removeClass('template')
85 | .toggleClass('active', i == displayPage)
86 | .find('a')
87 | .attr('href', '?p='+i+'&q='+encodeURIComponent(term))
88 | .data('page', i)
89 | .end()
90 | .find('.number')
91 | .text(i+1)
92 | .end()
93 | .insertBefore($pagingPostfix);
94 | }
95 |
96 | $paging
97 | .find('.next')
98 | .toggleClass('visible', displayPage < maxpages-1)
99 | .find('> a')
100 | .attr('href', '?p='+(displayPage+1)+'&q='+encodeURIComponent(term))
101 | .data('page', displayPage+1)
102 | .end()
103 | .end()
104 | .find('.prev')
105 | .toggleClass('visible', displayPage > 0)
106 | .find('> a')
107 | .attr('href', '?p='+(displayPage-1)+'&q='+encodeURIComponent(term))
108 | .data('page', displayPage-1)
109 | }
110 |
111 | if(res.hits.hits.length == 0) {
112 | $noresults.appendTo($list);
113 | }
114 | else {
115 | jQuery.each(res.hits.hits, function(idx, hit) {
116 | var
117 | quality = hit._score * 100 / res.hits.max_score,
118 | logourl = hit._source.conference.logo;
119 |
120 | if(logourl.match(/\.(png|jpg|jpeg|gif)$/)) {
121 | logourl = logourl.replace('//static.media.ccc.de/media/', '/images/logos/');
122 | logourl = logourl.substr(0, logourl.lastIndexOf('.')) + '.png';
123 | }
124 | else {
125 | logourl = '/images/logos/unknown.png';
126 | }
127 |
128 |
129 | var $item = $template
130 | .clone()
131 | .appendTo($list)
132 | .attr('data-quality', quality) // .data() does not show up in the DOM and this is mainly for debugging
133 | .addClass(
134 | quality > 80 ? 'high' :
135 | quality > 50 ? 'medium' :
136 | quality > 30 ? 'low' :
137 | 'nonsense'
138 | )
139 | .removeClass('template')
140 | .find('h3 .number')
141 | .text(displayPage * perPage + 1 + idx)
142 | .end()
143 | .find('h3 .t')
144 | .text(hit._source.event.title)
145 | .end()
146 | .find('img.conference-logo')
147 | .attr('alt', hit._source.conference.title)
148 | .attr('src', logourl)
149 | .end()
150 | .find('a.conference-url')
151 | .attr('href', hit._source.event.frontend_link)
152 | .end()
153 | .find('.recording_length .t')
154 | .text(Math.round(parseInt(hit._source.event.length) / 60)+' min')
155 | .end()
156 | .find('.date .t')
157 | .text(hit._source.event.date)
158 | .end()
159 | .find('.persons .t')
160 | .html(personlinks(hit._source.event.persons))
161 | .end()
162 | .find('.persons.fa')
163 | .addClass(hit._source.event.persons.length > 1 ? 'fa-group' : 'fa-user')
164 | .end();
165 | });
166 | }
167 |
168 | $form.addClass('fullsize');
169 | window.scrollTo(0, 0);
170 | if(triggerOrigin == 'param') {
171 | $results.removeClass('initial');
172 | }
173 |
174 | else if($form.hasClass('initial'))
175 | {
176 | $form.add($results).css({opacity: 0}).removeClass('initial').animate({
177 | opacity: 1
178 | })
179 | }
180 | }
181 | });
182 |
183 | if(triggerOrigin == 'param') {
184 | $form.removeClass('initial');
185 | }
186 | else if($form.hasClass('initial'))
187 | {
188 | $form.animate({
189 | opacity: 0
190 | }, {
191 | duration: 0.75
192 | })
193 | }
194 | })
195 | .on('click', '.paging a', function(e) {
196 | e.preventDefault();
197 | $search
198 | .find('.submit')
199 | .trigger('click', ['param', $(this).data('page')]);
200 | });
201 |
202 | var param = $.url().param();
203 | if(param.q) {
204 | $search
205 | .find('input.text')
206 | .val(param.q)
207 | .end()
208 | .find('.submit')
209 | .trigger('click', ['param', param.p]);
210 | }
211 |
212 | function personlinks(persons) {
213 | if(persons.length == 0) {
214 | return 'n/a';
215 | } else if(persons.length < 3) {
216 | return linkify_personnames(persons).join(' and ')
217 | } else {
218 | return linkify_personnames(persons.slice(0, -2)).join(', ') + ', ' + linkify_personnames(persons.slice(-2)).join(' and ')
219 | }
220 | }
221 |
222 | function linkify_personnames(personnames)
223 | {
224 | for (var i = 0; i < personnames.length; i++) {
225 | personnames[i] = $('')
226 | .attr({
227 | href: baseUrl+'?q='+encodeURIComponent(personnames[i]),
228 | title: 'Search for "'+personnames[i]+'"'
229 | })
230 | .text(personnames[i])
231 | .prop('outerHTML');
232 | };
233 |
234 | return personnames;
235 | }
236 |
237 | });
238 |
--------------------------------------------------------------------------------
/testdata.sql.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccc/media-frontend/3e0ec0c10a169a4af1096392c1b3ec2942750a75/testdata.sql.gz
--------------------------------------------------------------------------------