├── container ├── container-entrypoint.sh ├── favicon.xpm └── whatcanidoforfedora-web.conf ├── requirements.txt ├── .coafile ├── static ├── jquery.uls │ ├── img │ │ ├── clear.png │ │ ├── close.png │ │ ├── search.png │ │ ├── world_map.png │ │ ├── icon-language.png │ │ ├── clear.svg │ │ ├── close.svg │ │ ├── search.svg │ │ ├── icon-language.svg │ │ └── world_map.svg │ ├── css │ │ ├── jquery.uls.compact.css │ │ ├── jquery.uls.lcd.css │ │ ├── jquery.uls.grid.css │ │ ├── jquery.uls.css │ │ └── jquery.uls.mobile.css │ └── js │ │ ├── jquery.uls.regionfilter.js │ │ ├── jquery.uls.languagefilter.js │ │ ├── jquery.uls.lcd.js │ │ ├── jquery.uls.core.js │ │ ├── jquery.uls.data.utils.js │ │ └── jquery.uls.data.js ├── themes │ └── fedora │ │ ├── img │ │ └── sidebar-logo.png │ │ └── css │ │ └── site.css └── site.js ├── .gitignore ├── questions ├── includes │ └── fedora │ │ ├── i18n.yml │ │ ├── coding │ │ ├── scala.yml │ │ ├── haskell.yml │ │ ├── c++.yml │ │ ├── java.yml │ │ ├── c.yml │ │ ├── python.yml │ │ └── web.yml │ │ ├── server.yml │ │ ├── packaging.yml │ │ ├── advocacy.yml │ │ ├── cloud.yml │ │ ├── desktop.yml │ │ ├── community.yml │ │ ├── translation.yml │ │ ├── qa.yml │ │ ├── design.yml │ │ ├── coding.yml │ │ └── writing.yml └── fedora.yml ├── docker-compose.yml ├── .travis.yml ├── tests └── test_yaml.py ├── compile-translations.sh ├── setup.py ├── Dockerfile ├── asknot-ng.py ├── README.md ├── templates └── index.html └── asknot_lib.py /container/container-entrypoint.sh: -------------------------------------------------------------------------------- 1 | exec httpd -DFOREGROUND -DNO_DETACH 2 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | mako 2 | PyYAML 3 | 4 | # nose is only required for testing 5 | nose2 6 | -------------------------------------------------------------------------------- /.coafile: -------------------------------------------------------------------------------- 1 | [Default] 2 | bears = SpaceConsistencyBear 3 | use_spaces = true 4 | files = **/*.(html|css) 5 | -------------------------------------------------------------------------------- /static/jquery.uls/img/clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/asknot-ng/develop/static/jquery.uls/img/clear.png -------------------------------------------------------------------------------- /static/jquery.uls/img/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/asknot-ng/develop/static/jquery.uls/img/close.png -------------------------------------------------------------------------------- /static/jquery.uls/img/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/asknot-ng/develop/static/jquery.uls/img/search.png -------------------------------------------------------------------------------- /static/jquery.uls/img/world_map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/asknot-ng/develop/static/jquery.uls/img/world_map.png -------------------------------------------------------------------------------- /static/jquery.uls/img/icon-language.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/asknot-ng/develop/static/jquery.uls/img/icon-language.png -------------------------------------------------------------------------------- /static/themes/fedora/img/sidebar-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedora-infra/asknot-ng/develop/static/themes/fedora/img/sidebar-logo.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /*.html 2 | *.svg 3 | *.pyc 4 | *.egg-info 5 | build 6 | *.mo 7 | env 8 | venv 9 | /.venv 10 | .zanata-cache 11 | container/l10n.conf 12 | -------------------------------------------------------------------------------- /questions/includes/fedora/i18n.yml: -------------------------------------------------------------------------------- 1 | tree: 2 | children: 3 | - title: i18n 4 | subtitle: the Fedora Internationalization subproject 5 | link: https://fedoraproject.org/wiki/I18N 6 | -------------------------------------------------------------------------------- /questions/includes/fedora/coding/scala.yml: -------------------------------------------------------------------------------- 1 | tree: 2 | children: 3 | - title: Fedora Mobile 4 | subtitle: Interact with Fedora's community on the go! 5 | link: https://github.com/fedora-infra/mobile 6 | -------------------------------------------------------------------------------- /questions/includes/fedora/server.yml: -------------------------------------------------------------------------------- 1 | tree: 2 | children: 3 | - title: Server SIG 4 | subtitle: the special-interest-group for Fedora Server 5 | link: https://fedoraproject.org/wiki/SIGs/Server 6 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | 3 | services: 4 | web: 5 | build: 6 | context: . 7 | dockerfile: Dockerfile 8 | image: asknot-ng 9 | ports: 10 | - "8080:80" 11 | network_mode: "host" 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "3.9" 4 | cache: pip 5 | 6 | # command to install dependencies 7 | install: pip install nose2 PyYAML 8 | # command to run tests 9 | script: nose2 10 | 11 | notifications: 12 | email: false 13 | -------------------------------------------------------------------------------- /questions/includes/fedora/packaging.yml: -------------------------------------------------------------------------------- 1 | tree: 2 | children: 3 | - title: Package Maintainer 4 | subtitle: steward of the packages in Fedora 5 | link: https://docs.fedoraproject.org/en-US/package-maintainers/Joining_the_Package_Maintainers/ 6 | -------------------------------------------------------------------------------- /questions/includes/fedora/coding/haskell.yml: -------------------------------------------------------------------------------- 1 | tree: 2 | children: 3 | - title: Haskell Special Interest Group 4 | subtitle: Work with the community to make Haskell development in Fedora be awesome! 5 | link: https://fedoraproject.org/wiki/Haskell_SIG 6 | -------------------------------------------------------------------------------- /questions/includes/fedora/advocacy.yml: -------------------------------------------------------------------------------- 1 | tree: 2 | children: 3 | - title: Ambassador 4 | subtitle: the representatives of Fedora 5 | image: https://badges.fedoraproject.org/pngs/fas-ambassador.png 6 | link: https://fedoraproject.org/wiki/Ambassadors#Who_are_we.3F 7 | -------------------------------------------------------------------------------- /static/jquery.uls/img/clear.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /questions/includes/fedora/cloud.yml: -------------------------------------------------------------------------------- 1 | tree: 2 | children: 3 | - title: Cloud SIG 4 | subtitle: the special-interest-group for Fedora Cloud 5 | link: https://fedoraproject.org/wiki/Cloud_SIG 6 | - title: RDO 7 | subtitle: the OpenStack distro for Fedora 8 | link: https://www.rdoproject.org/documentation/onboarding/ 9 | -------------------------------------------------------------------------------- /questions/includes/fedora/coding/c++.yml: -------------------------------------------------------------------------------- 1 | tree: 2 | children: 3 | - title: LibreOffice 4 | subtitle: a free and open source office suite 5 | link: https://www.libreoffice.org/about-us/source-code/ 6 | 7 | - title: KDE 8 | subtitle: an integrated set of cross-platform applications 9 | link: https://community.kde.org/Get_Involved/development 10 | -------------------------------------------------------------------------------- /static/jquery.uls/img/close.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/jquery.uls/img/search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_yaml.py: -------------------------------------------------------------------------------- 1 | import os, fnmatch 2 | import yaml 3 | 4 | def validate_yaml_file(filename): 5 | print("Validating {0}".format(filename)) 6 | try: 7 | load_yaml = yaml.load(open(filename, 'r'), Loader=yaml.BaseLoader) 8 | except (yaml.YAMLError) as err: 9 | raise ValueError("Invalid YAML file: {0}".format(filename)) 10 | 11 | 12 | def test_yaml(): 13 | # Check YAML files for errors 14 | for root, dirs, files in os.walk('questions'): 15 | for filename in fnmatch.filter(files, "*.yml"): 16 | validate_yaml_file(os.path.join(root, filename)) 17 | -------------------------------------------------------------------------------- /compile-translations.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | 3 | ASKNOT_LOCALE_DIR=${ASKNOT_LOCALE_DIR:-l10n/fedora/locale} 4 | rm -f container/l10n.conf 5 | 6 | for locale in $(ls $ASKNOT_LOCALE_DIR/*.po); do 7 | echo $locale; 8 | locale=$(basename $locale) 9 | locale=${locale%.po}; 10 | mkdir -p $ASKNOT_LOCALE_DIR/$locale/LC_MESSAGES/; 11 | msgfmt -o $ASKNOT_LOCALE_DIR/$locale/LC_MESSAGES/asknot-ng.mo $ASKNOT_LOCALE_DIR/$locale.po; 12 | 13 | printf "RewriteCond %%{HTTP:Accept-Language} ^${locale//_/$'-'} [NC]\n" >> container/l10n.conf 14 | printf "RewriteRule \"^/$\" \"/$locale/\" [L,R]\n\n" >> container/l10n.conf 15 | done 16 | 17 | -------------------------------------------------------------------------------- /questions/includes/fedora/coding/java.yml: -------------------------------------------------------------------------------- 1 | tree: 2 | children: 3 | - title: Java Special Interest Group 4 | subtitle: Work with the community on Java packaging and development 5 | link: https://fedoraproject.org/wiki/SIGs/Java 6 | 7 | - title: JBoss 8 | subtitle: A suite of enterprise middleware 9 | link: https://tools.jboss.org/getinvolved/ 10 | 11 | - title: fedmsg-java 12 | subtitle: Interact with fedmsg from the JVM 13 | link: https://github.com/fedora-infra/fedmsg-java 14 | 15 | - title: jenkins-fedmsg-emit 16 | subtitle: Send fedmsg messages related to Jenkins builds 17 | link: https://github.com/fedora-infra/jenkins-fedmsg-emit 18 | -------------------------------------------------------------------------------- /questions/includes/fedora/coding/c.yml: -------------------------------------------------------------------------------- 1 | tree: 2 | children: 3 | - title: OSTree 4 | subtitle: git for operating system binaries 5 | link: https://ostreedev.github.io/ostree/ 6 | 7 | - title: systemd 8 | subtitle: widely renowned init system and suite of building blocks 9 | link: https://www.freedesktop.org/wiki/Software/systemd/ 10 | 11 | - title: Cockpit 12 | subtitle: a server manager that makes it easy to administer via a web browser 13 | link: https://cockpit-project.org/ 14 | 15 | - title: GNOME 16 | subtitle: an easy and elegant way to use your computer 17 | link: https://gitlab.gnome.org/GNOME 18 | 19 | - title: The Linux Kernel 20 | subtitle: that thing that connects application software to the hardware of a computer 21 | link: https://kernelnewbies.org/FirstKernelPatch 22 | -------------------------------------------------------------------------------- /questions/includes/fedora/desktop.yml: -------------------------------------------------------------------------------- 1 | tree: 2 | children: 3 | - title: Workstation Working Group 4 | subtitle: the group behind Fedora Workstation 5 | link: https://docs.fedoraproject.org/en-US/workstation-working-group/ 6 | 7 | - title: Cinnamon SIG 8 | subtitle: for those excited about Cinnamon desktop environment spiciness in Fedora 9 | link: https://fedoraproject.org/wiki/Cinnamon_Spin 10 | 11 | - title: KDE SIG 12 | subtitle: to provide high-quality, usable KDE software packages 13 | link: https://fedoraproject.org/wiki/SIGs/KDE 14 | 15 | - title: LXDE SIG 16 | subtitle: to define a high-quality LXDE experience for Fedora users 17 | link: https://fedoraproject.org/wiki/LXDE_SIG 18 | 19 | - title: XFCE SIG 20 | subtitle: for those excited about integration of the Xfce desktop environment 21 | link: https://fedoraproject.org/wiki/SIGs/Xfce 22 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | description = 'Ask not what $ORG can do for you, but what you can do for $ORG' 4 | 5 | setup( 6 | name='asknot-ng', 7 | version='1.0', 8 | description=description, 9 | license='GPLv3+', 10 | author='Ralph Bean', 11 | author_email='rbean@redhat.com', 12 | url='https://github.com/fedora-infra/asknot-ng', 13 | install_requires=[ 14 | 'mako', 15 | 'PyYAML', 16 | ], 17 | extras_require={ 18 | 'tests': ['nose2'], 19 | }, 20 | packages=[], 21 | py_modules=['asknot_lib'], 22 | 23 | # This declares our special-case extractor to 'babel', a python l18n tool. 24 | entry_points=""" 25 | [babel.extractors] 26 | asknot = asknot_lib:extract 27 | """, 28 | 29 | # This further declares that babel should use our extractor on yaml files 30 | # in the questions/ directory. 31 | message_extractors={ 32 | "questions": [ 33 | ('**.yml', 'asknot', None), 34 | ] 35 | } 36 | ) 37 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM fedora:34 as builder 2 | 3 | COPY . /code 4 | WORKDIR /code 5 | 6 | RUN dnf -y install gettext python3-setuptools python3-pyyaml python3-mako python3-babel python3-pygraphviz && dnf clean all && python3 setup.py install && ./build.sh 7 | 8 | FROM fedora:34 9 | 10 | COPY --from=builder /code/build /var/www/html/ 11 | COPY --from=builder /code/container/l10n.conf /etc/httpd/conf/l10n.conf 12 | COPY container/favicon.xpm /var/www/html/static/image/favicon.xpm 13 | COPY container/whatcanidoforfedora-web.conf /etc/httpd/conf/httpd.conf 14 | RUN dnf -y install httpd && dnf clean all\ 15 | && chown apache:0 /etc/httpd/conf/httpd.conf \ 16 | && chmod g+r /etc/httpd/conf/httpd.conf \ 17 | && chown apache:0 /etc/httpd/conf/l10n.conf \ 18 | && chmod g+r /etc/httpd/conf/l10n.conf \ 19 | && chown apache:0 /var/log/httpd \ 20 | && chmod g+rwX /var/log/httpd \ 21 | && chown apache:0 /var/run/httpd \ 22 | && chmod g+rwX /var/run/httpd\ 23 | && chown -R apache:0 /var/www/html \ 24 | && chmod -R g+rwX /var/www/html 25 | EXPOSE 8080 26 | USER apache 27 | ADD container/container-entrypoint.sh /srv 28 | ENTRYPOINT ["bash", "/srv/container-entrypoint.sh"] 29 | -------------------------------------------------------------------------------- /questions/includes/fedora/coding/python.yml: -------------------------------------------------------------------------------- 1 | tree: 2 | children: 3 | - title: Anaconda 4 | subtitle: the OS installer and all its related projects 5 | link: https://fedoraproject.org/wiki/Anaconda/Contribute 6 | 7 | - title: ABRT 8 | subtitle: the Automatic Bug Reporting Tool 9 | link: https://github.com/abrt/abrt/wiki/overview 10 | 11 | - title: DNF 12 | subtitle: Dandified Yum (DNF) is a major rewrite of yum 13 | link: https://github.com/rpm-software-management/dnf 14 | 15 | - title: Firewalld 16 | subtitle: a dynamically managed firewall with support for network zones 17 | link: https://www.firewalld.org/contribute/ 18 | 19 | - title: PortingDB 20 | subtitle: a dynamic database of Python 2 packages needing to be updated to Python 3 21 | link: https://fedora.portingdb.xyz/ 22 | 23 | - title: Cockpit 24 | subtitle: a web interface for managing and administrating Linux web servers 25 | link: https://cockpit-project.org/ 26 | 27 | - title: Web Services 28 | subtitle: Fedora Infrastructure team for the win 29 | link: https://fedoraproject.org/wiki/Infrastructure/GettingStarted 30 | -------------------------------------------------------------------------------- /questions/includes/fedora/community.yml: -------------------------------------------------------------------------------- 1 | tree: 2 | children: 3 | - title: General 4 | subtitle: on how to join the Community Operations team? 5 | image: https://badges.fedoraproject.org/pngs/fpl-blessing.png 6 | link: https://docs.fedoraproject.org/en-US/commops/contribute/commops-landing/ 7 | 8 | - title: Specific 9 | subtitle: on different kinds of community tasks 10 | image: https://badges.fedoraproject.org/pngs/the_panda_is_in.png 11 | segue1: Excellent! 12 | segue2: How does this sound? 13 | 14 | children: 15 | - title: Ambassador 16 | subtitle: the representatives of Fedora 17 | link: https://fedoraproject.org/wiki/Ambassadors#Who_are_we.3F 18 | image: https://badges.fedoraproject.org/pngs/fas-ambassador.png 19 | - title: Community Blog 20 | subtitle: the central hub of all Fedora news, across the subprojects 21 | link: https://communityblog.fedoraproject.org/writing-community-blog-article/ 22 | - title: 5 Things in Fedora This Week 23 | subtitle: finding information for a weekly article about the happenings in Fedora 24 | link: https://docs.fedoraproject.org/en-US/commops/contribute/commops-landing/#toolbox 25 | -------------------------------------------------------------------------------- /questions/includes/fedora/translation.yml: -------------------------------------------------------------------------------- 1 | tree: 2 | children: 3 | - title: General 4 | subtitle: about how to join the L10N team? 5 | link: https://fedoraproject.org/wiki/L10N#Joining_the_Fedora_Localization_Project 6 | 7 | - title: Specific 8 | subtitle: about L10N tasks 9 | segue1: Let's get down to business. 10 | segue2: Perhaps you'd be interested in translating... 11 | 12 | children: 13 | - title: User Interfaces 14 | subtitle: there are some essential projects that need translating 15 | link: https://fedoraproject.org/wiki/L10N_GUI 16 | - title: Wiki Pages 17 | subtitle: usually from English to another language 18 | link: https://fedoraproject.org/wiki/Fedora_Project_Wiki:Translating 19 | - title: Documentation 20 | subtitle: all the guides and release notes 21 | link: https://docs.fedoraproject.org/en-US/fedora-docs/contributing-docs/ 22 | - title: GNOME 23 | subtitle: which has its own translation resources 24 | link: https://wiki.gnome.org/TranslationProject 25 | - title: KDE 26 | subtitle: also with its own translation process 27 | link: https://l10n.kde.org/docs/translation-howto/ 28 | -------------------------------------------------------------------------------- /container/favicon.xpm: -------------------------------------------------------------------------------- 1 | /* XPM */ 2 | static char * linux_xpm[] = { 3 | "20 20 55 1", 4 | " c None", 5 | ". c #51A1DB", 6 | "+ c #51A2DA", 7 | "@ c #52A2DA", 8 | "# c #51A2DB", 9 | "$ c #79B1DE", 10 | "% c #68AADC", 11 | "& c #71AFDE", 12 | "* c #CEE3F3", 13 | "= c #F2F6F8", 14 | "- c #F0F5F8", 15 | "; c #E2EEF7", 16 | "> c #6FAEDE", 17 | ", c #51A3DA", 18 | "' c #D4E7F4", 19 | ") c #C7DFF0", 20 | "! c #59A6DC", 21 | "~ c #62ABDE", 22 | "{ c #CBE1F1", 23 | "] c #DFEDF6", 24 | "^ c #7AB1DE", 25 | "/ c #F7F8F9", 26 | "( c #7EB4E0", 27 | "_ c #97C0E4", 28 | ": c #7AB2DE", 29 | "< c #F9FAFA", 30 | "[ c #92BDE3", 31 | "} c #B7D6ED", 32 | "| c #6AABDD", 33 | "1 c #A6CEEA", 34 | "2 c #F2F7FA", 35 | "3 c #FFFFFF", 36 | "4 c #FAFAFA", 37 | "5 c #F9F9FA", 38 | "6 c #8CC1E5", 39 | "7 c #A7CFEA", 40 | "8 c #E6F0F8", 41 | "9 c #6FB1E0", 42 | "0 c #52A3DA", 43 | "a c #EDF4F9", 44 | "b c #6DB0DF", 45 | "c c #F3F6FA", 46 | "d c #67ADDE", 47 | "e c #71B0DF", 48 | "f c #BBD9EE", 49 | "g c #E3EFF6", 50 | "h c #63ABDD", 51 | "i c #80B5E0", 52 | "j c #D7E8F4", 53 | "k c #E4F0F8", 54 | "l c #5AA7DC", 55 | "m c #B2D5EC", 56 | "n c #E7F0F7", 57 | "o c #EDF3F7", 58 | "p c #51A2D9", 59 | " ", 60 | " .+@@+# ", 61 | " ++++++++++ ", 62 | " ++++++++++++ ", 63 | " +++++++$$$%+++ ", 64 | " +++++++&*=-;>++, ", 65 | " ++++++%')!~{]%++ ", 66 | " +++++++^/(++_=%++@ ", 67 | " #++++++:<$++[}++++ ", 68 | " ++++%$$:<$++++++++ ", 69 | " +++|12<:3456++++++ ", 70 | " +++7890:3$++++++++ ", 71 | " ++$ab++:3$++++++++ ", 72 | " ++$cd++e-$+++++++ ", 73 | " ++$fghijk++++++++ ", 74 | " +++lmno*e+++++++ ", 75 | " ++++%$$$+++++++ ", 76 | " p++++++++++++# ", 77 | " ++++++++++, ", 78 | " "}; 79 | -------------------------------------------------------------------------------- /static/jquery.uls/img/icon-language.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/jquery.uls/css/jquery.uls.compact.css: -------------------------------------------------------------------------------- 1 | .uls-compact .uls-icon-close, 2 | .uls-compact .uls-title-region, 3 | .uls-compact .uls-map-block, 4 | .uls-compact #uls-settings-block { 5 | display: none !important; 6 | } 7 | 8 | .uls-compact.uls-menu { 9 | border-radius: 0; 10 | } 11 | 12 | .uls-compact .uls-search { 13 | background: white; 14 | border-top: none; 15 | padding: 0.8em 0; 16 | border-bottom-width: 1px; 17 | border-bottom-style: solid; 18 | border-bottom-color: #DDD; 19 | } 20 | 21 | .uls-compact .uls-filterinput, 22 | .uls-compact .uls-filterinput:focus { 23 | background-color: transparent; 24 | border: none; 25 | box-shadow: none; 26 | outline: none; 27 | font-size: 18px; 28 | left: 0; 29 | } 30 | 31 | .uls-compact .uls-language-list { 32 | background: #FCFCFC; 33 | height: 20em; 34 | } 35 | 36 | .uls-compact .uls-search-label { 37 | background-size: 25px; 38 | height: 26px; 39 | width: 26px; 40 | float: right; 41 | opacity: 0.8; 42 | } 43 | 44 | .uls-compact .uls-languagefilter-clear { 45 | margin-left: 0; 46 | } 47 | 48 | .uls-compact .uls-title-region a { 49 | color: #777; 50 | display: inline-block; 51 | margin: 15px 0 5px 19px; 52 | cursor: pointer; 53 | padding: 6px; 54 | text-decoration: none; 55 | font-size: 14px; 56 | border: 1px solid transparent; 57 | } 58 | 59 | .uls-compact .uls-title-region a:hover { 60 | color: #252525; 61 | background: #F0F0F0; 62 | border: 1px solid #DDD; 63 | border-radius: 3px; 64 | } 65 | 66 | .uls-compact .uls-title-region a:before { 67 | display: inline-block; 68 | width: 0; 69 | height: 0; 70 | border-right: 4px solid #777; 71 | border-top: 4px solid transparent; 72 | border-bottom: 4px solid transparent; 73 | content: ""; 74 | margin: 0 6px 0 0; 75 | } 76 | -------------------------------------------------------------------------------- /questions/includes/fedora/qa.yml: -------------------------------------------------------------------------------- 1 | tree: 2 | children: 3 | - title: General 4 | subtitle: about how to join the QA team? 5 | link: https://fedoraproject.org/wiki/QA/Join 6 | 7 | - title: Specific 8 | subtitle: about different QA and testing tasks 9 | segue1: Certainly. 10 | segue2: We need people to... 11 | 12 | children: 13 | - title: Validate Releases 14 | subtitle: when there's an upcoming release, that is 15 | link: https://fedoraproject.org/wiki/QA/Join#release-validation 16 | - title: Test Updates 17 | subtitle: before they hit the stable repo 18 | link: https://fedoraproject.org/wiki/QA:Updates_Testing#What_to_test.2C_testing.2C_and_reporting_results 19 | - title: Create Test Cases 20 | subtitle: otherwise we won't know what to test 21 | link: https://fedoraproject.org/wiki/QA:SOP_test_case_creation 22 | 23 | - title: Develop Tools 24 | subtitle: for continuous awesomeness 25 | segue1: I'm of the same mind. 26 | segue2: We need people to hack on 27 | 28 | children: 29 | - title: BlockerBugs 30 | subtitle: used to propose and track release blocking bugs 31 | link: https://qa.fedoraproject.org/blockerbugs/ 32 | - title: Bodhi 33 | subtitle: the Fedora Updates System 34 | link: https://github.com/fedora-infra/bodhi 35 | - title: fedora-easy-karma 36 | subtitle: a handsome tool to ease testing updates 37 | link: https://fedoraproject.org/wiki/Fedora_Easy_Karma 38 | 39 | - title: Take Over 40 | subtitle: some basically abandoned tasks 41 | segue1: That's huge of you. 42 | segue2: we need someone to head up... 43 | 44 | children: 45 | - title: Bug Triage 46 | subtitle: you'll need a heart of gold 47 | link: https://fedoraproject.org/wiki/QA/Join#triage 48 | -------------------------------------------------------------------------------- /questions/includes/fedora/design.yml: -------------------------------------------------------------------------------- 1 | tree: 2 | children: 3 | - title: Design Team 4 | subtitle: creatives serving as a design firm for the Fedora community 5 | image: https://badges.fedoraproject.org/pngs/artist-01-apprentice.png 6 | segue1: Ok. 7 | segue2: Would you like something more 8 | 9 | children: 10 | - title: General 11 | subtitle: on how to join the design team? 12 | image: https://badges.fedoraproject.org/pngs/artist-02-badger-of-urbino.png 13 | link: https://fedoraproject.org/wiki/Join_the_Design_Team 14 | 15 | - title: Specific 16 | subtitle: on different kinds of design tasks 17 | image: https://badges.fedoraproject.org/pngs/artist-03-birth-of-badger.png 18 | segue1: Excellent! 19 | segue2: How does this sound? 20 | 21 | children: 22 | - title: Artwork Requests 23 | subtitle: there are people in need of good taste -- you can help 24 | link: https://gitlab.com/fedora/design/team/requests/-/issues 25 | image: https://badges.fedoraproject.org/pngs/artist-04-putti.png 26 | - title: Hackergotchis 27 | subtitle: making cute cut-outs of people's faces 28 | image: https://badges.fedoraproject.org/pngs/artist-05-vitruvian-badger.png 29 | link: https://fedoraproject.org/wiki/Artwork/HackergotchiService 30 | - title: Fedora Badges 31 | subtitle: make awesome badge art that fuels badges.fp.o 32 | image: https://badges.fedoraproject.org/pngs/artist-06-creation-of-badger.png 33 | link: https://pagure.io/fedora-badges 34 | 35 | - title: Websites Team 36 | subtitle: web developers that build getfedora.org and other awesome sites 37 | image: https://badges.fedoraproject.org/pngs/rock-the-web.png 38 | children: coding/web.yml 39 | -------------------------------------------------------------------------------- /static/jquery.uls/css/jquery.uls.lcd.css: -------------------------------------------------------------------------------- 1 | .uls-lcd-region-section ul li:hover { 2 | background-color: #eaeff7; 3 | } 4 | 5 | .uls-lcd-region-section { 6 | margin-top: 10px; 7 | } 8 | 9 | /* Language list */ 10 | .uls-language-list { 11 | height: 17em; 12 | overflow: auto; 13 | width: auto; 14 | } 15 | 16 | .uls-language-block ul { 17 | margin: 0 0 1.5em; 18 | } 19 | 20 | .uls-language-list ul li { 21 | cursor: pointer; 22 | font-weight: normal; 23 | overflow: hidden; 24 | white-space: nowrap; 25 | 26 | /* 27 | * Some languages have long names for various reasons and we still want 28 | * them to appear on one line. 29 | * To make it work correctly, the directionality must be set correctly 30 | * on the item level. 31 | */ 32 | text-overflow: ellipsis; 33 | 34 | /* 35 | * The directionality (ltr/rtl) for each list item is set dynamically 36 | * as HTML attributes in JavaScript. Setting directionality also applies 37 | * alignment, but a list with mixed alignment is hard to read. 38 | * All items are therefore explicitly aligned to the left, including names 39 | * of right-to-left languages in left-to-right environment and vice versa. 40 | * As long as the directionality of the item is set correctly, the text 41 | * is readable. 42 | */ 43 | text-align: left; 44 | 45 | /* 46 | * We don't want any visible bullets in this list. 47 | */ 48 | list-style-image: none; 49 | list-style-type: none; 50 | } 51 | 52 | .uls-language-list strong { 53 | text-decoration: underline; 54 | } 55 | 56 | .uls-language-list a { 57 | font-weight: normal; 58 | text-decoration: none; 59 | color: #3366bb; 60 | font-size: 14px; 61 | line-height: 1.6em; 62 | } 63 | 64 | .uls-language-block { 65 | width: 100%; 66 | } 67 | 68 | .uls-no-results-view { 69 | color: #555; 70 | height: 100%; 71 | } 72 | 73 | .uls-no-found-more { 74 | font-size: 0.9em; 75 | background: #F8F8F8; 76 | width: 100%; 77 | margin-top: 1.6em; 78 | line-height: 1.6em; 79 | position: absolute; 80 | bottom: 0; 81 | left: 0; 82 | } 83 | 84 | .uls-no-found-more a { 85 | cursor: pointer; 86 | } 87 | -------------------------------------------------------------------------------- /questions/includes/fedora/coding.yml: -------------------------------------------------------------------------------- 1 | tree: 2 | children: 3 | - title: Python 4 | subtitle: a dynamic language for rapid development 5 | segue1: So, you enjoy traceback-driven development? What could go wrong? 6 | segue2: Are you interested in hacking on... 7 | children: coding/python.yml 8 | 9 | - title: C 10 | subtitle: close to the metal 11 | segue1: So, you think OOP is for hipsters? Let me tell you, back in my day... 12 | segue2: How about working on... 13 | children: coding/c.yml 14 | 15 | - title: Haskell 16 | subtitle: a nondynamic language for rapid development 17 | segue1: So, programming without side-effects is what you're all about? Sounds useful! 18 | segue2: How about... 19 | children: coding/haskell.yml 20 | 21 | - title: Java 22 | subtitle: a nondynamic language for nonrapid development 23 | segue1: So you're a believer in AbstractMethodFactoryBeans? Straightforward enough... 24 | segue2: How about... 25 | children: coding/java.yml 26 | 27 | - title: Scala 28 | subtitle: a nondynamic language for semirapid development 29 | segue1: So, just OOP or functional languages wouldn't do it? You had to have both, huh? 30 | segue2: How about working on... 31 | children: coding/scala.yml 32 | 33 | - title: JavaScript 34 | subtitle: a dynamic web-oriented language for backend and frontend 35 | segue1: So, you think having native integers is over-rated? What could go wrong? 36 | segue2: There's plenty to do. How about joining the.. 37 | children: 38 | - title: Websites Team 39 | subtitle: web developers that build getfedora.org and other awesome sites 40 | segue1: Great! 41 | segue2: Would you like something more 42 | children: coding/web.yml 43 | 44 | - title: Ruby 45 | subtitle: hashrockets and bare words 46 | link: https://fedoraproject.org/wiki/Ruby_SIG 47 | 48 | - title: C++ 49 | subtitle: imperative and object-oriented with the power of low-level memory manipulation 50 | segue1: So, you're not happy if your error messages are comprehensible? We have a place for you. 51 | segue2: Try digging into... 52 | children: coding/c++.yml 53 | -------------------------------------------------------------------------------- /questions/includes/fedora/coding/web.yml: -------------------------------------------------------------------------------- 1 | tree: 2 | 3 | children: 4 | - title: General 5 | subtitle: on how to join the websites team? 6 | image: https://badges.fedoraproject.org/pngs/rock-the-web.png 7 | link: https://fedoraproject.org/wiki/Websites/Join 8 | 9 | - title: Specific 10 | subtitle: on different kinds of website tasks 11 | image: https://badges.fedoraproject.org/pngs/rock-the-web.png 12 | segue1: That's my style too. 13 | segue2: How about working on the... 14 | 15 | children: 16 | - title: Release Site 17 | subtitle: "getfedora.org is awesome" 18 | image: https://badges.fedoraproject.org/pngs/rock-the-web.png 19 | link: https://fedoraproject.org/wiki/Websites 20 | - title: Magazine 21 | subtitle: "did you even know we had a magazine?" 22 | image: https://badges.fedoraproject.org/pngs/extra!-extra!.png 23 | link: https://fedoraproject.org/wiki/Magazine 24 | - title: Wiki Theme 25 | subtitle: it's a little crusty 26 | image: https://badges.fedoraproject.org/pngs/junior-editor.png 27 | link: "https://fedoraproject.org/wiki/Websites#Wiki_Theme_Update" 28 | - title: General Theme 29 | subtitle: there's a list of sites that need standardizing 30 | image: https://badges.fedoraproject.org/pngs/rock-the-web.png 31 | link: "https://fedoraproject.org/wiki/Websites#All_Fedora_Websites_Theme_Update" 32 | - title: Translations 33 | subtitle: we're an international community after all 34 | image: https://badges.fedoraproject.org/pngs/rock-the-web.png 35 | link: https://fedoraproject.org/wiki/Websites#Translations 36 | - title: User Gallery 37 | subtitle: gallery for mini-interview-style user profiles and stories 38 | image: https://badges.fedoraproject.org/pngs/wiki-let-me-introduce-myself.png 39 | link: https://fedoraproject.org/wiki/Websites#User_gallery 40 | - title: AskNot Site 41 | subtitle: this thing -- the page you're looking at right now! 42 | image: https://badges.fedoraproject.org/pngs/rock-the-web.png 43 | link: https://github.com/fedora-infra/asknot-ng 44 | -------------------------------------------------------------------------------- /container/whatcanidoforfedora-web.conf: -------------------------------------------------------------------------------- 1 | 2 | Alias /favicon.ico /var/www/html/static/image/favicon.xpm 3 | 4 | AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css application/x-javascript 5 | 6 | FileETag MTime Size 7 | 8 | ExpiresActive On 9 | ExpiresDefault "access plus 30 minutes" 10 | 11 | RewriteEngine On 12 | Include /etc/httpd/conf/l10n.conf 13 | RewriteRule "^/$" "/en/" [R] 14 | RewriteRule "^/index.html$" "/en/" [R] 15 | 16 | ServerRoot "/etc/httpd" 17 | 18 | Listen 8080 19 | 20 | Include conf.modules.d/*.conf 21 | 22 | User apache 23 | Group apache 24 | 25 | ServerAdmin root@localhost 26 | 27 | 28 | AllowOverride none 29 | Require all denied 30 | 31 | 32 | DocumentRoot "/var/www/html" 33 | 34 | 35 | AllowOverride None 36 | # Allow open access: 37 | Require all granted 38 | 39 | 40 | 41 | Options Indexes FollowSymLinks 42 | AllowOverride None 43 | Require all granted 44 | 45 | 46 | 47 | DirectoryIndex index.html 48 | 49 | 50 | Require all denied 51 | 52 | 53 | ErrorLog "logs/error_log" 54 | 55 | LogLevel warn 56 | 57 | 58 | LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined 59 | LogFormat "%h %l %u %t \"%r\" %>s %b" common 60 | 61 | 62 | LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio 63 | 64 | 65 | CustomLog "logs/access_log" combined 66 | 67 | 68 | 69 | ScriptAlias /cgi-bin/ "/var/www/cgi-bin/" 70 | 71 | 72 | 73 | AllowOverride None 74 | Options None 75 | Require all granted 76 | 77 | 78 | 79 | TypesConfig /etc/mime.types 80 | AddType application/x-compress .Z 81 | AddType application/x-gzip .gz .tgz 82 | AddType text/html .shtml 83 | AddOutputFilter INCLUDES .shtml 84 | 85 | 86 | AddDefaultCharset UTF-8 87 | 88 | 89 | MIMEMagicFile conf/magic 90 | 91 | 92 | #EnableMMAP off 93 | EnableSendfile on 94 | 95 | # Load config files in the "/etc/httpd/conf.d" directory, if any. 96 | IncludeOptional conf.d/*.conf 97 | -------------------------------------------------------------------------------- /questions/includes/fedora/writing.yml: -------------------------------------------------------------------------------- 1 | tree: 2 | children: 3 | - title: Documentation 4 | subtitle: so people know how to use this crazy stuff 5 | segue1: Without you no-one would be able to "RTFM", as they say. 6 | segue2: Would you be interested in something... 7 | 8 | children: 9 | - title: General 10 | subtitle: about how to join the docs team? 11 | link: https://fedoraproject.org/wiki/Join_the_Docs_Project 12 | 13 | - title: Specific 14 | subtitle: about documentation related tasks 15 | segue1: Now we're talking! 16 | segue2: Let's see. How about working on... 17 | 18 | children: 19 | - title: Wiki Gardening 20 | subtitle: it tends to get a little overgrown 21 | link: https://fedoraproject.org/wiki/Wiki_gardening_tasks 22 | - title: A Guide 23 | subtitle: a more technical type of documentation 24 | link: https://fedoraproject.org/wiki/Category:Docs_Project_tasks#Guides 25 | - title: Real Issues 26 | subtitle: that people have reported 27 | link: https://bugzilla.redhat.com/buglist.cgi?bug_status=NEW&bug_status=ASSIGNED&classification=Fedora&list_id=3214328&product=Fedora%20Documentation&query_format=advanced 28 | 29 | - title: Blogging 30 | subtitle: 21st century journalism, my friend 31 | segue1: Only the news that's fit to print. 32 | segue2: How about something... 33 | children: 34 | - title: More Formal 35 | subtitle: something "official" 36 | segue1: Yes! The spotlight! 37 | segue2: You should get involved in writing for... 38 | children: 39 | - title: Fedora Magazine 40 | subtitle: all Fedora news, all the Fedora time 41 | link: https://fedoramagazine.org/writing-an-article-for-the-fedora-magazine/ 42 | - title: Less Formal 43 | subtitle: something more personal 44 | segue1: We have just the thing for you. 45 | segue2: Get your own blog added to the... 46 | children: 47 | - title: Community Blog 48 | subtitle: the central hub of all Fedora news, across the subprojects 49 | link: https://communityblog.fedoraproject.org/writing-community-blog-article/ 50 | - title: Fedora Planet 51 | subtitle: an aggregator of Fedora bloggers 52 | link: https://fedoraproject.org/wiki/Planet 53 | -------------------------------------------------------------------------------- /static/site.js: -------------------------------------------------------------------------------- 1 | // Setup our own endsWith definition since midori doesn't know about it. 2 | String.prototype.endsWith = function(suffix) { 3 | return this.indexOf(suffix, this.length - suffix.length) !== -1; 4 | }; 5 | 6 | function hashSelect(first) { 7 | console.log("Changing to new hash..."); 8 | var found = false; 9 | $.each(all_ids, function(i, idx) { 10 | var curr = $('#' + idx); 11 | if (location.href.endsWith(SEP + idx)) { 12 | curr.removeClass('hidden'); 13 | found = true; 14 | } 15 | else { 16 | var isHidden = curr.hasClass('hidden'); 17 | if (! isHidden) { 18 | curr.addClass('hidden'); 19 | } 20 | } 21 | }); 22 | if (! found) { 23 | $("#" + first).removeClass('hidden'); 24 | var original = location.href.replace(/\/$/, ""); 25 | history.pushState({}, '', original + SEP + first); 26 | } 27 | } 28 | 29 | $(document).ready(function() { 30 | // First thing.. hide the warning about javascript being required. 31 | $("#js-warning").addClass('hidden'); 32 | 33 | var first = question_tree.children[0].id; 34 | hashSelect(first); 35 | 36 | // Wire up the "yes" links 37 | $("a.yes").click(function(event) { 38 | $(this).parent().parent().addClass('hidden'); 39 | var next = $(this).attr('data-next'); 40 | $('#' + next).removeClass('hidden'); 41 | var original = location.href.replace(/\/$/, ""); 42 | history.pushState({}, '', original + SEP + next); 43 | }); 44 | 45 | // Wire up the "nope" links 46 | $("a.nope").click(function(event) { 47 | $(this).parent().parent().addClass('hidden'); 48 | var next = $(this).attr('data-next'); 49 | $('#' + next).removeClass('hidden'); 50 | var tokens = location.href.replace(/\/$/, "").split(SEP).slice(0, -1); 51 | tokens.push(next); 52 | history.replaceState({}, '', tokens.join(SEP)); 53 | }); 54 | 55 | // Wire up the "back" links 56 | $("a.back").click(function(event) { 57 | $(this).parent().parent().addClass('hidden'); 58 | var tokens = location.href.replace(/\/$/, "").split(SEP).slice(0, -1); 59 | var next = tokens.slice(-1).pop(); 60 | history.go(-1); 61 | $('#' + next).removeClass('hidden'); 62 | }); 63 | $(window).on('hashchange', function() { 64 | // Detect hash changes for "back" functions 65 | hashSelect(first); 66 | }); 67 | 68 | }); 69 | function reloadHome() { 70 | window.location = "#"; 71 | window.location.reload(); 72 | } 73 | -------------------------------------------------------------------------------- /asknot-ng.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ asknot-ng.py [OPTIONS] template.html questions.yml path/to/locale 3 | 4 | Ask not what $ORG can do for you... but what can you do for $ORG. 5 | """ 6 | 7 | from __future__ import print_function 8 | 9 | import argparse 10 | import copy 11 | import gettext 12 | import json 13 | import os 14 | import shutil 15 | import sys 16 | import traceback 17 | 18 | from asknot_lib import ( 19 | defaults, 20 | load_template, 21 | load_yaml, 22 | prepare_tree, 23 | gather_ids, 24 | produce_graph, 25 | ) 26 | 27 | 28 | def work(question_filename, template, lang, languages, graph, build, _, **kw): 29 | """ Main work function. Called once per 'lang' from ``main``. 30 | 31 | The function does all the things needed to build a copy of the site. 32 | Shortly, it: 33 | - loads the template used to render the html 34 | - loads the tree of questions from a questions file 35 | - recursively pulls in any other included questions files 36 | - prepares the tree by adding unique ids to each node 37 | - renders the template using the in-memory copy of the questions 38 | - writes out a copy of the rendered html to disk 39 | """ 40 | 41 | template = load_template(template) 42 | 43 | data = load_yaml(question_filename) 44 | 45 | data['tree'] = prepare_tree(data, data['tree'], _=_) 46 | data['all_ids'] = list(gather_ids(data['tree'])) 47 | data['all_ids_as_json'] = json.dumps(data['all_ids'], indent=4) 48 | data['tree_as_json'] = json.dumps(data['tree'], indent=4) 49 | 50 | kwargs = copy.copy(defaults) 51 | kwargs.update(data) 52 | kwargs.update(kw) 53 | kwargs['lang'] = lang 54 | kwargs['languages'] = languages 55 | 56 | if graph: 57 | dot = produce_graph(kwargs['tree']) 58 | dot.layout()#prog='dot') 59 | filename = '%s.svg' % kwargs.get('theme', 'asknot') 60 | dot.draw(filename) 61 | print("Wrote", filename) 62 | 63 | html = template.render(**kwargs) 64 | 65 | outdir = os.path.join(build, lang) 66 | 67 | if not os.path.exists(outdir): 68 | os.makedirs(outdir) 69 | 70 | outfile = os.path.join(outdir, 'index.html') 71 | with open(outfile, 'wb') as f: 72 | f.write(html) 73 | print("Wrote", outfile) 74 | 75 | 76 | def main(localedir, languages, strict, build, static, **kw): 77 | """ Main entry point for for the command line tool. 78 | 79 | This function loops over all translated copies of the site that it can find 80 | and renders a copy of the site for each language by calling ``work``. 81 | """ 82 | if languages is None: 83 | languages = [ 84 | d for d in os.listdir(localedir) 85 | if os.path.isdir(os.path.join(localedir, d))] 86 | 87 | # Default to english.. 88 | if 'en' not in languages and not strict: 89 | languages = languages + ['en'] 90 | else: 91 | languages = languages.split(',') 92 | 93 | if not languages: 94 | print("No languages found.") 95 | 96 | fallback = not strict 97 | for lang in languages: 98 | try: 99 | translation = gettext.translation( 100 | 'asknot-ng', localedir, languages=[lang], fallback=fallback) 101 | except IOError: 102 | traceback.print_exc() 103 | raise IOError("No translation found for %r" % lang) 104 | except ValueError: 105 | print("Got the following error for language %r" % lang) 106 | traceback.print_exc() 107 | continue 108 | translation.install() 109 | 110 | 111 | if sys.version_info[0] == 3: 112 | _ = translation.gettext 113 | else: 114 | _ = translation.ugettext 115 | 116 | work(_=_, lang=lang, languages=languages, build=build, **kw) 117 | 118 | staticdir = os.path.abspath(static) 119 | global_staticdir = os.path.join(build, "static") 120 | shutil.copytree(staticdir, global_staticdir, symlinks=True, dirs_exist_ok=True) 121 | print("Copied %s to %s" % (staticdir, global_staticdir)) 122 | 123 | 124 | def process_args(): 125 | parser = argparse.ArgumentParser(__doc__) 126 | parser.add_argument("template", help="Path to a mako template " 127 | "for the site.") 128 | parser.add_argument("question_filename", help="Path to a .yaml file " 129 | "containing the config and question tree.") 130 | parser.add_argument("localedir", help="Location of the locale directory.") 131 | parser.add_argument("-t", "--theme", default="default", 132 | help="Theme name to use.") 133 | parser.add_argument("-s", "--static", default="static", 134 | help="Directory of static files (js, css..).") 135 | parser.add_argument("-b", "--build", default="build", 136 | help="Directory to write output.") 137 | parser.add_argument("-l", "--languages", default=None, 138 | help="List of languages to use. Defaults to all.") 139 | parser.add_argument("-S", "--strict", default=False, action="store_true", 140 | help="Fail if no translation is found.") 141 | parser.add_argument("-g", "--graph", default=False, action="store_true", 142 | help="Also generate a graph of the question tree.") 143 | return parser.parse_args() 144 | 145 | 146 | if __name__ == '__main__': 147 | args = process_args() 148 | args = vars(args) 149 | main(**args) 150 | -------------------------------------------------------------------------------- /questions/fedora.yml: -------------------------------------------------------------------------------- 1 | title: What can I do for Fedora? 2 | description: What can I do for Fedora? 3 | favicon: https://getfedora.org/static/images/favicon.ico 4 | google-site-verification: a-S_XZt6rQej92LXTbscezXa1RLTN1KoaLoPUyvwAGM 5 | negatives: 6 | - "No" 7 | - Nope, nope, nope 8 | - Nope 9 | - No, thanks 10 | - Not on your life 11 | - Doesn't sound like me 12 | - Absolutely not 13 | - Next, please 14 | affirmatives: 15 | - "Yes" 16 | - Sounds awesome 17 | - Tell me more 18 | - That's me! 19 | - Totally 20 | - Sure 21 | backlinks: 22 | - I was wrong, take me back 23 | - I think I made a mistake 24 | - Get me outta here! 25 | 26 | navlinks: 27 | - name: Home 28 | link: "javascript:reloadHome();" 29 | - name: Get Fedora 30 | link: https://getfedora.org 31 | - name: CoC 32 | link: https://docs.fedoraproject.org/en-US/project/code-of-conduct/ 33 | - name: Chat 34 | link: https://docs.fedoraproject.org/en-US/fedora-join/#communication 35 | 36 | tree: 37 | segue1: Want to help Fedora? Tell me... 38 | segue2: what's your area of interest? 39 | children: 40 | - title: Design! 41 | subtitle: pixel ninjas 42 | image: https://badges.fedoraproject.org/pngs/design-team.png 43 | segue1: So you enjoy working on the way users interact with websites and programs? 44 | segue2: How about the... 45 | children: includes/fedora/design.yml 46 | 47 | - title: Coding 48 | subtitle: hacking the gibson 49 | image: https://badges.fedoraproject.org/pngs/fas-trust-me-i-know-what-i-am-doing.png 50 | segue1: "Zeroes and ones #allday? Me too." 51 | segue2: What's your favorite language? 52 | children: includes/fedora/coding.yml 53 | 54 | - title: Community Ops 55 | subtitle: connecting the dots 56 | image: https://badges.fedoraproject.org/pngs/the-write-stuff.png 57 | segue1: Have a knack for community-building? 58 | segue2: Would you like something more 59 | children: includes/fedora/community.yml 60 | 61 | - title: Writing 62 | subtitle: wordsmithery 63 | image: https://badges.fedoraproject.org/pngs/the-write-stuff.png 64 | segue1: So, you have a way with words? 65 | segue2: Would you be more interested in... 66 | children: includes/fedora/writing.yml 67 | 68 | - title: Translation 69 | subtitle: we're an international effort, after all 70 | image: https://badges.fedoraproject.org/pngs/irc-support-sig_member.png 71 | segue1: So you know more than one language? We need you! 72 | segue2: Would you be more interested in... 73 | children: includes/fedora/translation.yml 74 | 75 | - title: Advocacy 76 | subtitle: spreading the good word 77 | image: https://badges.fedoraproject.org/pngs/fedora-ambassador-mentor.png 78 | segue1: So you're a people person? 79 | segue2: How about becoming a Fedora... 80 | children: includes/fedora/advocacy.yml 81 | 82 | - title: Packaging 83 | subtitle: getting new software into Fedora 84 | image: https://badges.fedoraproject.org/pngs/copr-build.png 85 | segue1: So you want to help build the OS? Excellent. 86 | segue2: Then you'll need to become a Fedora... 87 | children: includes/fedora/packaging.yml 88 | 89 | - title: QA and Testing 90 | subtitle: as they say, "if it's not tested, it's broken" 91 | image: https://badges.fedoraproject.org/pngs/kernel-tester.png 92 | segue1: So you like to push all the buttons and break all the things? 93 | segue2: Are you more interested in something... 94 | children: includes/fedora/qa.yml 95 | 96 | - title: Modularity 97 | subtitle: rethinking how we compose the distro 98 | # image: https://docs.pagure.org/modularity/_static/logo.png 99 | link: https://docs.fedoraproject.org/en-US/modularity/community/ 100 | 101 | - title: The Desktop 102 | subtitle: for those who are passionate about user and developer experience 103 | # image: https://getfedora.org/static/images/logo-color-workstation.png 104 | segue1: So you're into making the OS polished and easy to use? 105 | segue2: You probably want to join the Fedora... 106 | children: includes/fedora/desktop.yml 107 | 108 | - title: The Server 109 | subtitle: the best and latest for the datacenter 110 | # image: https://getfedora.org/static/images/logo-color-server.png 111 | segue1: So you're into making the OS powerful and flexible? 112 | segue2: You probably want to join the Fedora... 113 | children: includes/fedora/server.yml 114 | 115 | - title: The Cloud 116 | subtitle: it's where the future lives 117 | # image: https://getfedora.org/static/images/logo-color-cloud.png 118 | segue1: High-minded? 119 | segue2: You probably want to join the Fedora... 120 | children: includes/fedora/cloud.yml 121 | 122 | - title: Internationalization 123 | subtitle: making Fedora work better for all languages 124 | image: https://badges.fedoraproject.org/pngs/irc-support-sig_member.png 125 | segue1: Have a regional or world perspective? 126 | segue2: You probably want to join the Fedora... 127 | children: includes/fedora/i18n.yml 128 | 129 | - title: Instruction 130 | image: https://badges.fedoraproject.org/pngs/sensei.png 131 | subtitle: spread your knowledge to others in the community 132 | link: https://fedoraproject.org/wiki/Classroom 133 | -------------------------------------------------------------------------------- /static/jquery.uls/css/jquery.uls.grid.css: -------------------------------------------------------------------------------- 1 | /* Generated using Foundation http://foundation.zurb.com/docs/grid.php */ 2 | /* Global Reset & Standards ---------------------- */ 3 | .grid * { 4 | -webkit-box-sizing: border-box; 5 | -moz-box-sizing: border-box; 6 | box-sizing: border-box; 7 | } 8 | 9 | /* Misc ---------------------- */ 10 | .grid .left { 11 | float: left; 12 | } 13 | 14 | .grid .right { 15 | float: right; 16 | } 17 | 18 | .grid .text-left { 19 | text-align: left; 20 | } 21 | 22 | .grid .text-right { 23 | text-align: right; 24 | } 25 | 26 | .grid .text-center { 27 | text-align: center; 28 | } 29 | 30 | .grid .hide { 31 | display: none; 32 | } 33 | 34 | .grid .highlight { 35 | background: #ffff99; 36 | } 37 | 38 | /* The Grid ---------------------- */ 39 | .grid .row { 40 | width: 100%; 41 | max-width: none; 42 | min-width: 600px; 43 | margin: 0 auto; 44 | } 45 | 46 | .grid .row .row { 47 | width: auto; 48 | max-width: none; 49 | min-width: 0; 50 | margin: 0 -5px; 51 | } 52 | 53 | .grid .row.collapse .column, 54 | .grid .row.collapse .columns { 55 | padding: 0; 56 | } 57 | 58 | .grid .row .row { 59 | width: auto; 60 | max-width: none; 61 | min-width: 0; 62 | margin: 0 -5px; 63 | } 64 | 65 | .grid .row .row.collapse { 66 | margin: 0; 67 | } 68 | 69 | .grid .column, .grid .columns { 70 | float: left; 71 | min-height: 1px; 72 | padding: 0 5px; 73 | position: relative; 74 | } 75 | 76 | .grid .column.centered, .grid .columns.centered { 77 | float: none; 78 | margin: 0 auto; 79 | } 80 | 81 | .grid .row .one { 82 | width: 8.333%; 83 | } 84 | 85 | .grid .row .two { 86 | width: 16.667%; 87 | } 88 | 89 | .grid .row .three { 90 | width: 25%; 91 | } 92 | 93 | .grid .row .four { 94 | width: 33.333%; 95 | } 96 | 97 | .grid .row .five { 98 | width: 41.667%; 99 | } 100 | 101 | .grid .row .six { 102 | width: 50%; 103 | } 104 | 105 | .grid .row .seven { 106 | width: 58.333%; 107 | } 108 | 109 | .grid .row .eight { 110 | width: 66.667%; 111 | } 112 | 113 | .grid .row .nine { 114 | width: 75%; 115 | } 116 | 117 | .grid .row .ten { 118 | width: 83.333%; 119 | } 120 | 121 | .grid .row .eleven { 122 | width: 91.667%; 123 | } 124 | 125 | .grid .row .twelve { 126 | width: 100%; 127 | } 128 | 129 | .grid .row .offset-by-one { 130 | margin-left: 8.333%; 131 | } 132 | 133 | .grid .row .offset-by-two { 134 | margin-left: 16.667%; 135 | } 136 | 137 | .grid .row .offset-by-three { 138 | margin-left: 25%; 139 | } 140 | 141 | .grid .row .offset-by-four { 142 | margin-left: 33.333%; 143 | } 144 | 145 | .grid .row .offset-by-five { 146 | margin-left: 41.667%; 147 | } 148 | 149 | .grid .row .offset-by-six { 150 | margin-left: 50%; 151 | } 152 | 153 | .grid .row .offset-by-seven { 154 | margin-left: 58.333%; 155 | } 156 | 157 | .grid .row .offset-by-eight { 158 | margin-left: 66.667%; 159 | } 160 | 161 | .grid .row .offset-by-nine { 162 | margin-left: 75%; 163 | } 164 | 165 | .grid .row .offset-by-ten { 166 | margin-left: 83.333%; 167 | } 168 | 169 | .grid .push-two { 170 | left: 16.667%; 171 | } 172 | 173 | .grid .pull-two { 174 | right: 16.667%; 175 | } 176 | 177 | .grid .push-three { 178 | left: 25%; 179 | } 180 | 181 | .grid .pull-three { 182 | right: 25%; 183 | } 184 | 185 | .grid .push-four { 186 | left: 33.333%; 187 | } 188 | 189 | .grid .pull-four { 190 | right: 33.333%; 191 | } 192 | 193 | .grid .push-five { 194 | left: 41.667%; 195 | } 196 | 197 | .grid .pull-five { 198 | right: 41.667%; 199 | } 200 | 201 | .grid .push-six { 202 | left: 50%; 203 | } 204 | 205 | .grid .pull-six { 206 | right: 50%; 207 | } 208 | 209 | .grid .push-seven { 210 | left: 58.333%; 211 | } 212 | 213 | .grid .pull-seven { 214 | right: 58.333%; 215 | } 216 | 217 | .grid .push-eight { 218 | left: 66.667%; 219 | } 220 | 221 | .grid .pull-eight { 222 | right: 66.667%; 223 | } 224 | 225 | .grid .push-nine { 226 | left: 75%; 227 | } 228 | 229 | .grid .pull-nine { 230 | right: 75%; 231 | } 232 | 233 | .grid .push-ten { 234 | left: 83.333%; 235 | } 236 | 237 | .grid .pull-ten { 238 | right: 83.333%; 239 | } 240 | 241 | /* Nicolas Gallagher's micro clearfix */ 242 | .grid .row { 243 | *zoom: 1; 244 | } 245 | 246 | .grid .row:before, .grid .row:after { 247 | content: ""; 248 | display: table; 249 | } 250 | 251 | .grid .row:after { 252 | clear: both; 253 | } 254 | 255 | /* Block Grids ---------------------- */ 256 | /* These are 2-up, 3-up, 4-up and 5-up ULs, suited 257 | for repeating blocks of content. Add 'mobile' to 258 | them to switch them just like the layout grid 259 | (one item per line) on phones 260 | 261 | For IE7/8 compatibility block-grid items need to be 262 | the same height. You can optionally uncomment the 263 | lines below to support arbitrary height, but know 264 | that IE7/8 do not support :nth-child. 265 | -------------------------------------------------- */ 266 | .grid .block-grid { 267 | display: block; 268 | overflow: hidden; 269 | padding: 0; 270 | } 271 | 272 | .grid .block-grid > li { 273 | display: block; 274 | height: auto; 275 | float: left; 276 | } 277 | 278 | .grid .block-grid.two-up { 279 | margin: 0 -15px; 280 | } 281 | 282 | .grid .block-grid.two-up > li { 283 | width: 50%; 284 | padding: 0 15px 15px; 285 | } 286 | 287 | /* .block-grid.two-up>li:nth-child(2n+1) {clear: left;} */ 288 | .grid .block-grid.three-up { 289 | margin: 0 -12px; 290 | } 291 | 292 | .grid .block-grid.three-up > li { 293 | width: 33.33%; 294 | padding: 0 12px 12px; 295 | } 296 | 297 | /* .block-grid.three-up>li:nth-child(3n+1) {clear: left;} */ 298 | .grid .block-grid.four-up { 299 | margin: 0 -10px; 300 | } 301 | 302 | .grid .block-grid.four-up > li { 303 | width: 25%; 304 | padding: 0 10px 10px; 305 | } 306 | 307 | /* .block-grid.four-up>li:nth-child(4n+1) {clear: left;} */ 308 | .grid .block-grid.five-up { 309 | margin: 0 -8px; 310 | } 311 | 312 | .grid .block-grid.five-up > li { 313 | width: 20%; 314 | padding: 0 8px 8px; 315 | } 316 | -------------------------------------------------------------------------------- /static/jquery.uls/js/jquery.uls.regionfilter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jQuery region filter plugin. 3 | * 4 | * Copyright (C) 2012 Alolita Sharma, Amir Aharoni, Arun Ganesh, Brandon Harris, 5 | * Niklas Laxström, Pau Giner, Santhosh Thottingal, Siebrand Mazeland and other 6 | * contributors. See CREDITS for a list. 7 | * 8 | * UniversalLanguageSelector is dual licensed GPLv2 or later and MIT. You don't 9 | * have to do anything special to choose one license or the other and you don't 10 | * have to notify anyone which license you are using. You are free to use 11 | * UniversalLanguageSelector in commercial projects as long as the copyright 12 | * header is left intact. See files GPL-LICENSE and MIT-LICENSE for details. 13 | * 14 | * @file 15 | * @ingroup Extensions 16 | * @licence GNU General Public Licence 2.0 or later 17 | * @licence MIT License 18 | */ 19 | 20 | ( function ( $ ) { 21 | 'use strict'; 22 | 23 | /* RegionSelector plugin definition */ 24 | 25 | var RegionSelector; 26 | 27 | /** 28 | * Region selector is a language selector based on regions. 29 | * Usage: $( 'jqueryselector' ).regionselector( options ); 30 | * The attached element should have data-regiongroup attribute 31 | * that defines the regiongroup for the selector. 32 | */ 33 | RegionSelector = function ( element, options ) { 34 | this.$element = $( element ); 35 | this.options = $.extend( {}, $.fn.regionselector.defaults, options ); 36 | this.$element.addClass( 'regionselector' ); 37 | this.regions = []; 38 | this.cache = null; 39 | this.regionGroup = this.$element.data( 'regiongroup' ); 40 | this.init(); 41 | this.listen(); 42 | }; 43 | 44 | RegionSelector.prototype = { 45 | constructor: RegionSelector, 46 | 47 | init: function () { 48 | var region = this.$element.data( 'region' ); 49 | this.regions = $.uls.data.getRegionsInGroup( this.regionGroup ); 50 | 51 | if ( region ) { 52 | this.regions.push( region ); 53 | } 54 | }, 55 | 56 | test: function ( langCode ) { 57 | var region, i, 58 | langRegions = $.uls.data.getRegions( langCode ); 59 | 60 | for ( i = 0; i < this.regions.length; i++ ) { 61 | region = this.regions[i]; 62 | 63 | if ( $.inArray( region, langRegions ) >= 0 ) { 64 | this.render( langCode, region ); 65 | this.cache[langCode] = region; 66 | 67 | return; 68 | } 69 | } 70 | }, 71 | 72 | show: function () { 73 | var result, languagesByScriptGroup, scriptGroup, languages, i, 74 | $element = this.options.$target && this.options.$target.$element, 75 | $parent = $element && $element.parent(), 76 | $prev = $element && $element.prev(); 77 | 78 | if ( $element && $parent ) { 79 | // Avoid reflows while adding new elements to the list 80 | // Use .detach() to keep jQuery events and data associated with elements 81 | $element.detach(); 82 | } 83 | 84 | if ( this.cache ) { 85 | // If the result cache is present, render the results from there. 86 | //noinspection JSUnusedAssignment 87 | result = null; 88 | 89 | for ( result in this.cache ) { 90 | this.render( result, this.cache[result] ); 91 | } 92 | } else { 93 | this.cache = {}; 94 | // Get the languages grouped by script group 95 | languagesByScriptGroup = $.uls.data.getLanguagesByScriptGroup( this.options.languages ); 96 | 97 | // Make sure that we go by the original order 98 | // of script groups 99 | for ( scriptGroup in $.uls.data.scriptgroups ) { 100 | // Get the languages for the script group 101 | languages = languagesByScriptGroup[scriptGroup]; 102 | 103 | // It's possible that some script groups are missing 104 | if ( !languages ) { 105 | continue; 106 | } 107 | 108 | // Sort it based on autonym 109 | languages.sort( $.uls.data.sortByAutonym ); 110 | 111 | for ( i = 0; i < languages.length; i++ ) { 112 | // Check whether it belongs to the region 113 | this.test( languages[i] ); 114 | } 115 | } 116 | } 117 | 118 | if ( $element && $parent ) { 119 | // Restore the element to where we removed it from 120 | if ( $prev ) { 121 | $prev.after( $element ); 122 | } else { 123 | $parent.append( $element ); 124 | } 125 | } 126 | 127 | if ( this.options.success ) { 128 | this.options.success( this ); 129 | } 130 | }, 131 | 132 | render: function ( langCode, region ) { 133 | var $target = this.options.$target; 134 | 135 | if ( !$target ) { 136 | return; 137 | } 138 | 139 | $target.append( langCode, region ); 140 | }, 141 | 142 | listen: function () { 143 | this.$element.on( 'click', $.proxy( this.click, this ) ); 144 | }, 145 | 146 | click: function () { 147 | var $list, $firstTargetRegion; 148 | 149 | // Don't do anything if a region is selected already 150 | if ( this.$element.hasClass( 'active' ) ) { 151 | return; 152 | } 153 | 154 | $list = this.options.$target.$element; 155 | $firstTargetRegion = $list.find( '#' + this.regions[0] ); 156 | 157 | // Scroll to appropriate area 158 | $list.scrollTop( 159 | $firstTargetRegion.offset().top - $list.offset().top + $list.scrollTop() 160 | ); 161 | 162 | // Make the selected region (and it only) active 163 | $( '.regionselector' ).removeClass( 'active' ); 164 | 165 | if ( this.regionGroup ) { 166 | // if there is a region group, make it active. 167 | this.$element.addClass( 'active' ); 168 | } 169 | } 170 | }; 171 | 172 | /* RegionSelector plugin definition */ 173 | 174 | $.fn.regionselector = function ( option ) { 175 | return this.each( function () { 176 | var $this = $( this ), 177 | data = $this.data( 'regionselector' ), 178 | options = typeof option === 'object' && option; 179 | 180 | if ( !data ) { 181 | $this.data( 'regionselector', ( data = new RegionSelector( this, options ) ) ); 182 | } 183 | 184 | if ( typeof option === 'string' ) { 185 | data[option](); 186 | } 187 | } ); 188 | }; 189 | 190 | $.fn.regionselector.defaults = { 191 | $target: null, // Where to render the results 192 | success: null, // callback if any results found. 193 | noresults: null, // callback when no results to show 194 | languages: null 195 | }; 196 | 197 | $.fn.regionselector.Constructor = RegionSelector; 198 | } ( jQuery ) ); 199 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # asknot-ng 2 | 3 | [![Build Status](https://travis-ci.org/fedora-infra/asknot-ng.svg)](https://travis-ci.org/fedora-infra/asknot-ng) 4 | 5 | Ask not what `$ORG` can do for you, but what you can do for `$ORG`. 6 | 7 | Written by [@ralphbean][threebean]. Inspired by [the original work][wcidfm] of 8 | [Josh Matthews][jdm], [Henri Koivuneva][wham], and [others][asknot-contribs]. 9 | 10 | I stumbled upon and loved the original [whatcanidoformozilla.org][wcidfm] and 11 | wanted to deploy it for the [Fedora Community][fedora] but I found that I 12 | couldn’t easily change the questions and links that were presented. A year 13 | went by and in 2015 I wrote this: “asknot-ng”. 14 | 15 | The gist of this “next generation” rewrite is to make it as configurable as 16 | possible. There is a primary script, ``asknot-ng.py`` 17 | that works like a static-site generator. It takes as input three things: 18 | 19 | - A questions file, written in yaml (see the [example][example-questions] or 20 | [Fedora’s file][fedora-questions]). You’ll have to write your own one of 21 | these. 22 | - A template file, written in mako (the [default][default-template] should work 23 | for everybody). 24 | - A ‘theme’ argument to specify what CSS to use. The default is nice enough, 25 | but you’ll probably want to customize it to your own use case. 26 | 27 | We have a [Fedora instance up and running][wcidff] if you’d like to poke it. 28 | 29 | [![Translation status](https://translate.fedoraproject.org/widgets/fedora-infra/-/asknot-ng/287x66-grey.png)](https://translate.fedoraproject.org/engage/fedora-infra/?utm_source=widget) 30 | 31 | ## Tools 32 | 33 | 1. HTML5 - The Structure. 34 | 2. CSS3 - The Style. 35 | 3. Javascript - The Functions. 36 | 4. Jquery - JavaScript libraries to select, remove, clone, and modify different elements on the page. 37 | 5. Bootstrap - Bootstrap is a front end framework used to design responsive web pages and applications. 38 | 39 | ## Requirements 40 | 41 | The site-generator script is written in Python, so you’ll need that. 42 | Furthermore, see [requirements.txt][requirements] or just run:: 43 | 44 | $ sudo dnf install python-mako PyYAML python-virtualenv 45 | 46 | The script can optionally generate an svg visualizing your question tree. This 47 | requires pygraphviz which you could install like so: 48 | 49 | $ sudo dnf install python-pygraphviz 50 | 51 | ## Giving it a run 52 | 53 | Install the requirements, first. 54 | 55 | Clone the repo:: 56 | 57 | $ git clone https://github.com/fedora-infra/asknot-ng.git 58 | $ cd asknot-ng 59 | 60 | Create a virtualenv into which you can install the module. 61 | 62 | $ virtualenv --system-site-packages venv 63 | $ source venv/bin/activate 64 | $ python setup.py develop 65 | 66 | Run the script with the Fedora configuration:: 67 | 68 | $ ./asknot-ng.py templates/index.html questions/fedora.yml l10n/fedora/locale --theme fedora 69 | Wrote build/en/index.html 70 | 71 | and open up `build/en/index.html` in your favorite browser. 72 | 73 | ## Preparing Translations 74 | 75 | First, setup a virtualenv, install Babel, and build the egg info. 76 | 77 | $ virtualenv venv 78 | $ source venv/bin/activate 79 | $ pip install Babel 80 | $ python setup.py develop 81 | 82 | Then, extract the translatable strings: 83 | 84 | $ python setup.py extract_messages --output-file l10n/fedora/locale/asknot-ng.pot --input-dir=. 85 | 86 | ## Container 87 | 88 | Asknot can be build and released as a container, to do so you can use the provided Dockerfile. 89 | 90 | ###### Releasing a container 91 | 92 | ``` 93 | podman build -t asknot . 94 | ``` 95 | 96 | The Dockerfile makes use of multistage container build, meaning that in a first stage a container is used to prepare the translations and build the static pages then the static content is copied to a second container which is used to serve this content. 97 | 98 | ###### Running Container 99 | 100 | ``` 101 | podman run --name=asknot -d -p 8080:80 --net=host localhost/asknot 102 | ``` 103 | 104 | ###### Composing Container 105 | 106 | Asknot can be build and released as a container, in other similar way to do so you can use the provided Dockerfile-compose file. 107 | 108 | ``` 109 | podman-compose up -d 110 | ``` 111 | 112 | ###### Verifiying 113 | 114 | In your Favorite Browser Just type: 115 | 116 | ``` 117 | localhost:8080 118 | ``` 119 | 120 | ## Application Deployment 121 | 122 | ``asknot-ng`` currently runs on Fedora infrastructure Openshift instance. There are 2 deployments one in [staging] and one in [production]. 123 | 124 | The deployment of new version to these environment is managed from the github repository, thanks to the following 2 branches ``staging`` and ``production``. 125 | 126 | ### Staging 127 | 128 | To deploy a change to the staging environment you need to push the commits to the ``staging`` branch, then Openshift will trigger a build using the Dockerfile located 129 | in this repository and deploy the new application. 130 | 131 | ### Production 132 | 133 | To deploy a change in the production environment you need to push the commits to the ``production`` branch, then Openshift will trigger a build using the Dockerfile located 134 | in this repository and deploy the new application. 135 | 136 | ## Contributing back 137 | 138 | ``asknot-ng`` is licensed GPLv3+ and we’d love to get patches back containing 139 | even the things you might not think we want. If you have a questions file for 140 | your repo, a modified template, or a CSS theme for your use case, please 141 | [send them to us][patches]. It would be nice to build a library of deployments 142 | so we can all learn. 143 | 144 | **Note**: While the application is licensed GPLv3+, The [Fedora 22 wallpaper](static/themes/fedora/img/background.png) used is licensed under a *Creative Commons Attribution 4 License*. 145 | 146 | Of course, bug reports and patches to the main script are appreciated as 147 | always. 148 | 149 | Happy Hacking! 150 | 151 | [threebean]: http://threebean.org 152 | [fedora]: http://getfedora.org 153 | [example-questions]: https://github.com/fedora-infra/asknot-ng/blob/develop/questions/example.yml 154 | [fedora-questions]: https://github.com/fedora-infra/asknot-ng/blob/develop/questions/fedora.yml 155 | [default-template]: https://github.com/fedora-infra/asknot-ng/blob/develop/templates/index.html 156 | [requirements]: https://github.com/fedora-infra/asknot-ng/blob/develop/requirements.txt 157 | [patches]: https://help.github.com/articles/editing-files-in-another-user-s-repository/ 158 | [wcidfm]: https://web.archive.org/web/20220714110613/https://www.whatcanidoformozilla.org/ 159 | [wcidff]: http://whatcanidoforfedora.org 160 | [jdm]: http://www.joshmatthews.net 161 | [wham]: http://wham.fi 162 | [asknot-contribs]: https://github.com/jdm/asknot/contributors 163 | [staging]: https://stg.whatcanidoforfedora.org/ 164 | [production]: https://whatcanidoforfedora.org/ 165 | -------------------------------------------------------------------------------- /static/themes/fedora/css/site.css: -------------------------------------------------------------------------------- 1 | a, 2 | a:focus, 3 | a:hover { 4 | color: #fff; 5 | } 6 | .bandana a, 7 | .bandana a:focus, 8 | .bandana a:hover { 9 | color: #27ae60; 10 | } 11 | 12 | /* Custom default button */ 13 | .btn-default, 14 | .btn-default:hover, 15 | .btn-default:focus { 16 | color: #333; 17 | text-shadow: none; /* Prevent inheritence from `body` */ 18 | background-color: #fff; 19 | border: 1px solid #fff; 20 | } 21 | 22 | html, 23 | body { 24 | height: 100%; 25 | background-color: white; 26 | background-size: 100% auto; 27 | } 28 | 29 | body { 30 | color: black; 31 | font-family: 'Open Sans'; 32 | font-style: normal; 33 | font-weight: 400; 34 | text-align: center; 35 | text-shadow: 0 1px 3px rgba(0,0,0,.5); 36 | } 37 | 38 | .site-wrapper { 39 | width: 100%; 40 | height: 100%; /* For at least Firefox */ 41 | min-height: 100%; 42 | /* -webkit-box-shadow: inset 0 0 75px rgba(0,0,0,.5); 43 | box-shadow: inset 0 0 75px rgba(0,0,0,.5); */ 44 | } 45 | 46 | .site-wrapper-inner { 47 | height: -webkit-calc(100% - 40px); 48 | height: -moz-calc(100% - 40px); 49 | height: calc(100% - 40px); 50 | } 51 | 52 | .cover-container { 53 | position: relative; 54 | top: 40%; 55 | -webkit-transform: translateY(-45%); 56 | -ms-transform: translateY(-45%); 57 | transform: translateY(-45%); 58 | width: 100%; 59 | } 60 | 61 | /* Padding for spacing */ 62 | .inner { 63 | padding: 30px; 64 | } 65 | 66 | #js-warning .panel-body { 67 | background-color: #3c6eb4; 68 | } 69 | 70 | /* 71 | * Header 72 | */ 73 | .masthead-brand { 74 | background: url('../img/sidebar-logo.png'); 75 | margin-bottom: 10px; 76 | margin-right: auto; 77 | margin-left: auto; 78 | display: block; 79 | width: 141px; 80 | height: 50px; 81 | background-repeat: no-repeat; 82 | 83 | text-indent: -9999px; 84 | 85 | } 86 | 87 | .masthead.clearfix{ 88 | background-color: #51a2da; 89 | border: 2px solid #51a2da; 90 | /* border-radius: 8px; */ 91 | } 92 | 93 | .masthead-nav > li { 94 | display: inline-block; 95 | } 96 | 97 | .masthead-nav > li + li { 98 | margin-left: 20px; 99 | } 100 | 101 | .masthead-nav > li > a { 102 | padding-right: 0; 103 | padding-left: 0; 104 | font-size: 16px; 105 | font-weight: bold; 106 | font-family: Montserrat; 107 | font-style: normal; 108 | font-weight: 400; 109 | /* color: #fff; /* IE8 proofing */ 110 | /* color: rgba(255,255,255,.75); */ 111 | color: black; 112 | border-bottom: 2px solid transparent; 113 | } 114 | 115 | .masthead-nav > li > a:hover, 116 | .masthead-nav > li > a:focus { 117 | background-color: transparent; 118 | border-bottom-color: #a9a9a9; 119 | border-bottom-color: rgba(255,255,255,.25); 120 | } 121 | 122 | .masthead-nav > .active > a, 123 | .masthead-nav > .active > a:hover, 124 | .masthead-nav > .active > a:focus, 125 | .masthead-nav > .open > a, 126 | .masthead-nav > .open > a:hover, 127 | .masthead-nav > .open > a:focus { 128 | color: #fff; 129 | background-color: transparent; 130 | border-bottom-color: #fff; 131 | } 132 | 133 | .dropdown-menu { 134 | min-width: 105px; 135 | } 136 | 137 | .cover .btn-lg { 138 | padding: 10px 20px; 139 | font-weight: bold; 140 | } 141 | 142 | .bandana { 143 | background-color: #fff; 144 | width: 100%; 145 | margin-bottom: 25px; 146 | padding: 20px; 147 | 148 | position: relative; 149 | overflow: hidden; 150 | } 151 | 152 | .main-text { 153 | vertical-align: middle; 154 | display: inline-block; 155 | /* This has to be relative just so z-index applies */ 156 | position: relative; 157 | z-index: 2; 158 | } 159 | 160 | .bandana .img { 161 | display: none; 162 | 163 | margin-top: -100px; 164 | margin-bottom: -100px; 165 | position: absolute; 166 | right: 10%; 167 | opacity: 0.5; 168 | z-index: 0; 169 | } 170 | 171 | .bandana .img img { 172 | z-index: -1; 173 | height: 400px; 174 | width: 400px 175 | } 176 | 177 | /* 178 | * Footer 179 | */ 180 | .mastfoot { 181 | color: #999; /* IE8 proofing */ 182 | color: rgba(255,255,255,.5); 183 | /* display: none; */ 184 | display: block; 185 | } 186 | 187 | .mastfoot { 188 | height: 40px; 189 | background-color: #51a2da; 190 | border: 2px solid #51a2da; 191 | /* border-radius: 8px; */ 192 | } 193 | 194 | .masthead, .mastfoot { 195 | margin-right: auto; 196 | margin-left: auto; 197 | } 198 | 199 | .title { 200 | font-size: 40pt; 201 | font-weight: 900; 202 | color: #294172; 203 | text-transform: uppercase; 204 | } 205 | 206 | .segue1 { 207 | font-size: 12pt; 208 | font-style: italic; 209 | margin-bottom: -10px; 210 | } 211 | 212 | .segue2 { 213 | font-size: 15pt; 214 | font-style: italic; 215 | } 216 | 217 | .subtitle { 218 | font-size: 15pt; 219 | font-style: italic; 220 | margin-top: -10px; 221 | padding-bottom: 12px; 222 | color: #3c6eb4; 223 | border-bottom: dashed 1px #3c6eb4; 224 | display: inline; 225 | } 226 | 227 | /* Extra small devices (phones, 600px and down) */ 228 | @media only screen and (max-width: 600px) { 229 | .title { font-size: 25px; } 230 | .segue1 { font-size: 15pt; } 231 | .segue2 { font-size: 15pt; } 232 | .subtitle { font-size: 10pt; } 233 | .bandana .img { display: inline-block; } 234 | .masthead-brand { float: top; } 235 | .masthead-nav { float: center; } 236 | .mastfoot { 237 | float: bottom; 238 | } 239 | .masthead.clearfix { 240 | width: 100%; 241 | height: 20%; 242 | } 243 | .inner { 244 | padding: 6px; 245 | } 246 | .btn-lg { 247 | font-size: 12px; 248 | } 249 | 250 | .masthead-nav > li > a { 251 | font-size: 12px; 252 | } 253 | } 254 | 255 | /* Small devices (portrait tablets and large phones, 600px and up) */ 256 | @media only screen and (min-width: 600px) { 257 | .title { font-size: 64pt; } 258 | .segue1 { font-size: 18pt; } 259 | .segue2 { font-size: 32pt; } 260 | .subtitle { font-size: 24pt; } 261 | .bandana .img { display: inline-block; } 262 | .masthead-brand { 263 | float: left; 264 | } 265 | .masthead-nav { 266 | float: center; 267 | } 268 | .mastfoot { 269 | float: bottom; 270 | } 271 | } 272 | 273 | /* Medium devices (landscape tablets, 768px and up) */ 274 | @media only screen and (min-width: 768px) { 275 | .title { font-size: 64pt; } 276 | .segue1 { font-size: 18pt; } 277 | .segue2 { font-size: 32pt; } 278 | .subtitle { font-size: 24pt; } 279 | .bandana .img { display: inline-block; } 280 | .masthead-brand { 281 | float: left; 282 | } 283 | .masthead-nav { 284 | float: center; 285 | } 286 | .mastfoot { 287 | float: bottom; 288 | } 289 | } 290 | 291 | /* Large devices (laptops/desktops, 992px and up) */ 292 | @media only screen and (min-width: 992px) { 293 | .title { font-size: 64pt; } 294 | .segue1 { font-size: 18pt; } 295 | .segue2 { font-size: 32pt; } 296 | .subtitle { font-size: 24pt; } 297 | .bandana .img { display: inline-block; } 298 | .masthead-brand { 299 | float: left; 300 | } 301 | .masthead-nav { 302 | float: center; 303 | } 304 | .mastfoot { 305 | float: bottom; 306 | } 307 | } 308 | 309 | /* Extra large devices (large laptops and desktops, 1200px and up) */ 310 | @media only screen and (min-width: 1200px) { 311 | .title { font-size: 64pt; } 312 | .segue1 { font-size: 18pt; } 313 | .segue2 { font-size: 32pt; } 314 | .subtitle { font-size: 24pt; } 315 | .bandana .img { display: inline-block; } 316 | .masthead-brand { 317 | float: left; 318 | } 319 | .masthead-nav { 320 | float: center; 321 | } 322 | .mastfoot { 323 | float: bottom; 324 | } 325 | } 326 | -------------------------------------------------------------------------------- /static/jquery.uls/css/jquery.uls.css: -------------------------------------------------------------------------------- 1 | .uls-trigger { 2 | /* @embed */ 3 | background: transparent url('../img/icon-language.png') no-repeat scroll left center; 4 | /* @embed */ 5 | background-image: -webkit-linear-gradient(transparent, transparent), url('../img/icon-language.svg'); 6 | /* @embed */ 7 | background-image: linear-gradient(transparent, transparent), url('../img/icon-language.svg'); 8 | padding-left: 30px; 9 | } 10 | 11 | .uls-menu { 12 | position: absolute; 13 | z-index: 1000; 14 | display: none; 15 | margin-top: 1px; 16 | /* Styling */ 17 | background-color: #ffffff; 18 | border: 1px solid #ccc; 19 | border: 1px solid rgba(0, 0, 0, 0.2); 20 | border-radius: 5px; 21 | -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); 22 | -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); 23 | box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); 24 | -webkit-background-clip: padding-box; 25 | -moz-background-clip: padding; 26 | background-clip: padding-box; 27 | } 28 | 29 | .uls-wide { 30 | min-width: 715px; 31 | width: 45%; 32 | } 33 | 34 | .uls-title-region a { 35 | padding-left: 15px; 36 | } 37 | 38 | .uls-menu .uls-title { 39 | font-weight: normal; 40 | border: none; 41 | padding-top: 1.25em; 42 | padding-left: 15px; 43 | padding-bottom: 3px; 44 | font-size: 18pt; 45 | line-height: 1.25em; 46 | color: #555; 47 | } 48 | 49 | .uls-menu .uls-no-results-found-title { 50 | font-size: 16pt; 51 | font-weight: bold; 52 | line-height: 1.5em; 53 | padding-left: 6px; 54 | padding-top: 10px; 55 | margin-top: 0; 56 | margin-bottom: 15px; 57 | border-bottom: none; 58 | color: #555; 59 | } 60 | 61 | .uls-menu .uls-lcd-region-section .uls-lcd-region-title { 62 | color: #777; 63 | font-size: 14pt; 64 | font-weight: lighter; 65 | line-height: 1.5em; 66 | padding-left: 0; 67 | margin-top: 0; 68 | margin-bottom: 10px; 69 | border-bottom: none; 70 | } 71 | 72 | .uls-worldmap { 73 | /* @embed */ 74 | background: transparent url('../img/world_map.png') no-repeat scroll right top; 75 | /* @embed */ 76 | background-image: -webkit-linear-gradient(transparent, transparent), url('../img/world_map.svg'); 77 | /* @embed */ 78 | background-image: linear-gradient(transparent, transparent), url('../img/world_map.svg'); 79 | background-size: 100%; 80 | } 81 | 82 | div.uls-region { 83 | cursor: pointer; 84 | padding: 0; 85 | margin: 0; 86 | height: 120px; 87 | border-bottom-color: transparent; 88 | border-bottom-style: solid; 89 | border-bottom-width: 2px; 90 | } 91 | 92 | .uls-worldmap .uls-region { /* The map doesn't flip */ 93 | /* @noflip */ 94 | float: left; 95 | } 96 | 97 | .uls-region a { 98 | bottom: 2px; 99 | left: 2px; 100 | padding: 0; 101 | position: absolute; 102 | font-size: 13px; 103 | line-height: 1.2em; 104 | text-decoration: none; 105 | overflow: hidden; 106 | text-overflow: ellipsis; 107 | width: 99%; 108 | } 109 | 110 | .uls-region:hover { 111 | /*Cross-browser background transparency*/ 112 | background: #3366bb; 113 | background: rgba(51, 102, 187, 0.1); 114 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#253366bb, endColorstr=#253366bb ); 115 | -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#253366bb, endColorstr=#253366bb)"; 116 | } 117 | 118 | .uls-map-block .active { 119 | border-bottom-color: #3366bb; 120 | border-bottom-style: solid; 121 | } 122 | 123 | .uls-menu .row .uls-map-block { 124 | top: 1px; 125 | margin-right: 0; 126 | padding-right: 0; 127 | float: right; 128 | overflow: hidden; 129 | opacity: 0.7; 130 | -moz-transition: opacity 0.2s linear; 131 | -o-transition: opacity 0.2s linear; 132 | -webkit-transition: opacity 0.2s linear; 133 | transition: opacity 0.2s linear; 134 | } 135 | 136 | .uls-map-block a { 137 | color: #333; 138 | opacity: 0; 139 | -moz-transition: opacity 0.15s linear; 140 | -o-transition: opacity 0.15s linear; 141 | -webkit-transition: opacity 0.15s linear; 142 | transition: opacity 0.15s linear; 143 | } 144 | 145 | .uls-menu .uls-map-block:hover, 146 | .uls-menu .uls-map-block:hover a { 147 | opacity: 1; 148 | color: #333; 149 | } 150 | 151 | .uls-map-block .uls-region-1 { 152 | border-color: transparent; 153 | } 154 | 155 | .uls-map-block:hover .active { 156 | border-color: #3366bb; 157 | } 158 | 159 | .uls-map-block .active a { 160 | font-weight: bold; 161 | } 162 | 163 | .uls-icon-close { 164 | /* @embed */ 165 | background: transparent url('../img/close.png') no-repeat scroll center center; 166 | /* @embed */ 167 | background-image: -webkit-linear-gradient(transparent, transparent), url('../img/close.svg'); 168 | /* @embed */ 169 | background-image: linear-gradient(transparent, transparent), url('../img/close.svg'); 170 | float: right; 171 | padding: 15px; 172 | cursor: pointer; 173 | } 174 | 175 | .uls-menu .uls-languagefilter { 176 | background-color: transparent; 177 | border: 1px solid #c9c9c9; 178 | border-radius: 2px 2px 2px 2px; 179 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1) inset; 180 | color: #333; 181 | display: block; 182 | padding: 6px; 183 | -moz-transition: border 0.15s linear 0s; 184 | -o-transition: border 0.15s linear 0s; 185 | -webkit-transition: border 0.15s linear 0s; 186 | transition: border 0.15s linear 0s; 187 | } 188 | 189 | .uls-menu .uls-languagefilter:focus { 190 | border: 1px solid #3366bb; 191 | } 192 | 193 | .uls-menu .uls-search { 194 | position: relative; 195 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#F0F0F0', endColorstr='#FBFBFB'); 196 | background: #f8f8f8; 197 | background: -webkit-gradient(linear, left top, left bottom, from(#F0F0F0), to(#FBFBFB)); 198 | background: -webkit-linear-gradient(top, #F0F0F0, #FBFBFB); 199 | background: -moz-linear-gradient(top, #F0F0F0, #FBFBFB); 200 | background: -o-linear-gradient(top, #F0F0F0, #FBFBFB); 201 | background: linear-gradient(#F0F0F0, #FBFBFB); 202 | border-top-color: #AAA; 203 | border-top-style: solid; 204 | border-top-width: 1px; 205 | padding: 0.8em 0; 206 | border-bottom-width: 1px; 207 | border-bottom-style: solid; 208 | border-bottom-color: #DDD; 209 | } 210 | 211 | .uls-menu .uls-search-label { 212 | /* @embed */ 213 | background: transparent url('../img/search.png') no-repeat scroll right center; 214 | /* @embed */ 215 | background-image: -webkit-linear-gradient(transparent, transparent), url('../img/search.svg'); 216 | /* @embed */ 217 | background-image: linear-gradient(transparent, transparent), url('../img/search.svg'); 218 | background-size: 30px; 219 | height: 32px; 220 | width: 32px; 221 | float: right; 222 | } 223 | 224 | .uls-menu .uls-languagefilter-clear { 225 | /* @embed */ 226 | background: transparent url('../img/clear.png') no-repeat scroll left center; 227 | /* @embed */ 228 | background-image: -webkit-linear-gradient(transparent, transparent), url('../img/clear.svg'); 229 | /* @embed */ 230 | background-image: linear-gradient(transparent, transparent), url('../img/clear.svg'); 231 | cursor: pointer; 232 | height: 32px; 233 | position: absolute; 234 | width: 32px; 235 | margin-left: -32px; 236 | } 237 | 238 | .uls-menu .uls-filterinput { 239 | position: absolute; 240 | top: 0; 241 | left: 0; 242 | font-size: 14px; 243 | height: 32px; 244 | width: 100%; 245 | text-align: left; 246 | } 247 | 248 | .uls-menu .uls-filtersuggestion { 249 | padding: 6px; 250 | background-color: white; 251 | color: #888; 252 | border: 1px transparent; 253 | border-radius: 2px 2px 2px 2px; 254 | box-shadow: 0 1px 2px transparent inset; 255 | left: 1px; 256 | } 257 | 258 | .uls-menu .uls-search-input-block { 259 | position: relative; 260 | } 261 | -------------------------------------------------------------------------------- /static/jquery.uls/css/jquery.uls.mobile.css: -------------------------------------------------------------------------------- 1 | @media only screen and (max-width: 767px) { 2 | 3 | .uls-mobile.uls-menu { 4 | width: 95%; 5 | left: 2.5%; 6 | } 7 | 8 | .uls-mobile .uls-language-list { 9 | -webkit-overflow-scrolling: touch; 10 | } 11 | 12 | .uls-mobile .uls-language-block { 13 | padding-left: 15px !important; 14 | } 15 | 16 | .uls-mobile .uls-language-block ul { 17 | min-height: 14em; 18 | } 19 | 20 | .uls-mobile .uls-language-block a { 21 | font-size: 16px; 22 | line-height: 1.7em; 23 | } 24 | 25 | .uls-mobile div.uls-region { 26 | width: 33% !important; 27 | float: left !important; 28 | } 29 | 30 | .uls-mobile .uls-map-block a, 31 | .uls-mobile .uls-map-block { 32 | opacity: 1 !important; 33 | } 34 | 35 | .uls-mobile .row { 36 | width: auto; 37 | min-width: 0; 38 | margin-left: 0; 39 | margin-right: 0; 40 | } 41 | 42 | .uls-mobile .column, 43 | .uls-mobile .columns { 44 | width: auto !important; 45 | float: none; 46 | } 47 | 48 | .uls-mobile .column:last-child, 49 | .uls-mobile .columns:last-child { 50 | float: none; 51 | } 52 | 53 | .uls-mobile [class*="column"] + [class*="column"]:last-child { 54 | float: none; 55 | } 56 | 57 | .uls-mobile .column:before, 58 | .uls-mobile .uls-mobile .columns:before, 59 | .uls-mobile .column:after, 60 | .columns:after { 61 | content: ""; 62 | display: table; 63 | } 64 | 65 | .uls-mobile .column:after, 66 | .uls-mobile .columns:after { 67 | clear: both; 68 | } 69 | 70 | .uls-mobile .offset-by-one, 71 | .uls-mobile .offset-by-two, 72 | .uls-mobile .offset-by-three, 73 | .uls-mobile .offset-by-four, 74 | .uls-mobile .offset-by-five, 75 | .uls-mobile .offset-by-six, 76 | .uls-mobile .offset-by-seven, 77 | .uls-mobile .offset-by-eight, 78 | .uls-mobile .offset-by-nine, 79 | .uls-mobile .offset-by-ten { 80 | margin-left: 0 !important; 81 | } 82 | 83 | .uls-mobile .push-two, 84 | .uls-mobile .push-three, 85 | .uls-mobile .push-four, 86 | .uls-mobile .push-five, 87 | .uls-mobile .push-six, 88 | .uls-mobile .push-seven, 89 | .uls-mobile .push-eight, 90 | .uls-mobile .push-nine, 91 | .uls-mobile .push-ten { 92 | left: auto; 93 | } 94 | 95 | .uls-mobile .pull-two, 96 | .uls-mobile .pull-three, 97 | .uls-mobile .pull-four, 98 | .uls-mobile .pull-five, 99 | .uls-mobile .pull-six, 100 | .uls-mobile .pull-seven, 101 | .uls-mobile .pull-eight, 102 | .uls-mobile .pull-nine, 103 | .uls-mobile .pull-ten { 104 | right: auto; 105 | } 106 | 107 | /* Mobile 4-column Grid */ 108 | .uls-mobile .row .mobile-one { 109 | width: 25% !important; 110 | float: left; 111 | padding: 0 4px; 112 | } 113 | 114 | .uls-mobile .row .mobile-one:last-child { 115 | float: right; 116 | } 117 | 118 | .uls-mobile .row.collapse .mobile-one { 119 | padding: 0; 120 | } 121 | 122 | .uls-mobile .row .mobile-two { 123 | width: 50% !important; 124 | float: left; 125 | padding: 0 4px; 126 | } 127 | 128 | .uls-mobile .row .mobile-two:last-child { 129 | float: right; 130 | } 131 | 132 | .uls-mobile .row.collapse .mobile-two { 133 | padding: 0; 134 | } 135 | 136 | .uls-mobile .row .mobile-three { 137 | width: 75% !important; 138 | float: left; 139 | padding: 0 4px; 140 | } 141 | 142 | .uls-mobile .row .mobile-three:last-child { 143 | float: right; 144 | } 145 | 146 | .uls-mobile .row.collapse .mobile-three { 147 | padding: 0; 148 | } 149 | 150 | .uls-mobile .row .mobile-four { 151 | width: 100% !important; 152 | float: left; 153 | padding: 0 4px; 154 | } 155 | 156 | .uls-mobile .row .mobile-four:last-child { 157 | float: right; 158 | } 159 | 160 | .uls-mobile .row.collapse .mobile-four { 161 | padding: 0; 162 | } 163 | 164 | .uls-mobile .push-one-mobile { 165 | left: 25%; 166 | } 167 | 168 | .uls-mobile .pull-one-mobile { 169 | right: 25%; 170 | } 171 | 172 | .uls-mobile .push-two-mobile { 173 | left: 50%; 174 | } 175 | 176 | .uls-mobile .pull-two-mobile { 177 | right: 50%; 178 | } 179 | 180 | .uls-mobile .push-three-mobile { 181 | left: 75%; 182 | } 183 | 184 | .uls-mobile .pull-three-mobile { 185 | right: 75%; 186 | } 187 | } 188 | 189 | /* Visibility Classes ---------------------- */ 190 | /* Standard (large) display targeting */ 191 | .uls-mobile .show-for-small, 192 | .uls-mobile .show-for-medium, 193 | .uls-mobile .show-for-medium-down, 194 | .uls-mobile .hide-for-large, 195 | .uls-mobile .hide-for-large-up, 196 | .uls-mobile .show-for-xlarge { 197 | display: none !important; 198 | } 199 | 200 | .uls-mobile .hide-for-xlarge, 201 | .uls-mobile .show-for-large, 202 | .uls-mobile .show-for-large-up, 203 | .uls-mobile .hide-for-small, 204 | .uls-mobile .hide-for-medium, 205 | .uls-mobile .hide-for-medium-down { 206 | display: block !important; 207 | } 208 | 209 | /* Very large display targeting */ 210 | @media only screen and (min-width: 1441px) { 211 | 212 | .uls-mobile .hide-for-small, 213 | .uls-mobile .hide-for-medium, 214 | .uls-mobile .hide-for-medium-down, 215 | .hide-for-large, .show-for-large-up, 216 | .show-for-xlarge { 217 | display: block !important; 218 | } 219 | 220 | .show-for-small, 221 | .uls-mobile .show-for-medium, 222 | .uls-mobile .show-for-medium-down, 223 | .uls-mobile .show-for-large, 224 | .uls-mobile .hide-for-large-up, 225 | .uls-mobile .hide-for-xlarge { 226 | display: none !important; 227 | } 228 | } 229 | /* Medium display targeting */ 230 | @media only screen and (max-width: 1279px) and (min-width: 768px) { 231 | 232 | .uls-mobile .hide-for-small, 233 | .uls-mobile .show-for-medium, 234 | .uls-mobile .show-for-medium-down, 235 | .uls-mobile .hide-for-large, 236 | .uls-mobile .hide-for-large-up, 237 | .uls-mobile .hide-for-xlarge { 238 | display: block !important; 239 | } 240 | 241 | .uls-mobile .show-for-small, 242 | .uls-mobile .hide-for-medium, 243 | .uls-mobile .hide-for-medium-down, 244 | .uls-mobile .show-for-large, 245 | .uls-mobile .show-for-large-up, 246 | .uls-mobile .show-for-xlarge { 247 | display: none !important; 248 | } 249 | } 250 | /* Small display targeting */ 251 | @media only screen and (max-width: 767px) { 252 | 253 | .uls-mobile .show-for-small, 254 | .uls-mobile .hide-for-medium, 255 | .uls-mobile .show-for-medium-down, 256 | .uls-mobile .hide-for-large, 257 | .uls-mobile .hide-for-large-up, 258 | .uls-mobile .hide-for-xlarge { 259 | display: block !important; 260 | } 261 | .uls-mobile .hide-for-small, 262 | .uls-mobile .show-for-medium, 263 | .uls-mobile .hide-for-medium-down, 264 | .uls-mobile .show-for-large, 265 | .uls-mobile .show-for-large-up, 266 | .uls-mobile .show-for-xlarge { 267 | display: none !important; 268 | } 269 | } 270 | 271 | /* Orientation targeting */ 272 | .uls-mobile .show-for-landscape, 273 | .uls-mobile .hide-for-portrait { 274 | display: block !important; 275 | } 276 | 277 | .uls-mobile .hide-for-landscape, 278 | .uls-mobile .show-for-portrait { 279 | display: none !important; 280 | } 281 | 282 | @media screen and (orientation: landscape) { 283 | .uls-mobile .show-for-landscape, 284 | .uls-mobile .hide-for-portrait { 285 | display: block !important; 286 | } 287 | .uls-mobile .hide-for-landscape, 288 | .uls-mobile .show-for-portrait { 289 | display: none !important; 290 | } 291 | } 292 | 293 | @media screen and (orientation: portrait) { 294 | .uls-mobile .show-for-portrait, 295 | .uls-mobile .hide-for-landscape { 296 | display: block !important; 297 | } 298 | .uls-mobile .hide-for-portrait, 299 | .uls-mobile .show-for-landscape { 300 | display: none !important; 301 | } 302 | } 303 | 304 | /* Touch-enabled device targeting */ 305 | .uls-mobile .show-for-touch { 306 | display: none !important; 307 | } 308 | 309 | .uls-mobile .hide-for-touch { 310 | display: block !important; 311 | } 312 | 313 | .uls-mobile .touch .show-for-touch { 314 | display: block !important; 315 | } 316 | 317 | .uls-mobile .touch .hide-for-touch { 318 | display: none !important; 319 | } 320 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | ${title} 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
48 | 49 |
50 | 51 |
52 |
53 |

${title}

54 | 65 |
66 |
67 | 68 |
69 | 70 |
71 |
72 |
73 | This page requires JavaScript 74 |
75 |
76 |

This page only works with JavaScript enabled.

77 |

If you have JavaScript disabled with a plugin like NoScript, please 78 | disable that plugin and reload the page. If you trust us, you 79 | might find it convenient to enable JavaScript just 80 | for this domain.

81 |

If you are opposed to JavaScript in principle, you're not left out! 82 | You can read the page source to get at all the data.

83 |
84 |
85 |
86 | 87 | <%def name="card(node, next, segue1=None, segue2=None, toplevel=False)"> 88 | 133 | 134 | % if 'children' in node: 135 | % for i, child in enumerate(node['children']): 136 | ${card(child, node['children'][(i + 1) % len(node['children'])]['id'], segue1=node.get('segue1'), segue2=node.get('segue2'))} 137 | % endfor 138 | % endif 139 | 140 | 141 | % for i, child in enumerate(tree['children']): 142 | ${card(child, tree['children'][(i + 1) % len(tree['children'])]['id'], segue1=tree.get('segue1'), segue2=tree.get('segue2'), toplevel=True)} 143 | % endfor 144 | 145 |
146 | 147 |
148 | 149 |
150 |

Generated by asknot-ng, 151 | written by Ralph Bean and others. 153 | Inspired by the 154 | original. 155 | Change Language

156 |
157 | 158 |
159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /asknot_lib.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ Utilities module used by the asknot-ng.py script. """ 3 | 4 | import hashlib 5 | import os 6 | import random 7 | import subprocess as sp 8 | import sys 9 | 10 | import mako.template 11 | import pkg_resources 12 | import yaml 13 | 14 | # Lists of translatable strings so we know what to extract at extraction time 15 | # and so we know what to translate at render time. 16 | translatable_collections = ['negatives', 'affirmatives', 'backlinks'] 17 | translatable_fields = ['title', 'description', 'segue1', 'segue2', 'subtitle'] 18 | 19 | if sys.version_info[0] == 2: 20 | string_types = (basestring,) 21 | else: 22 | string_types = (str, bytes,) 23 | 24 | 25 | def asknot_version(): 26 | try: 27 | return pkg_resources.get_distribution('asknot-ng').version 28 | except pkg_resources.DistributionNotFound: 29 | try: 30 | stdout = sp.check_output(['git', 'rev-parse', 'HEAD']) 31 | return stdout[:8] # Short hash 32 | except: 33 | return 'unknown' 34 | 35 | 36 | defaults = { 37 | 'title': 'asknot-ng', 38 | 'author': 'Ralph Bean', 39 | 'description': ( 40 | 'Ask not what $ORG can do for you, ' 41 | 'but what you can do for $ORG' 42 | ), 43 | 'asknot_version': asknot_version(), 44 | 'favicon': 'whatever', 45 | 'googlesiteverification': 'n/a', 46 | 'navlinks': [], 47 | 'negatives': ['No, thanks'], 48 | 'affirmatives': ['Yes, please'], 49 | 'backlinks': ['I was wrong, take me back'], 50 | 'SEP': '#', # Make this '/' for cool prod environments 51 | } 52 | 53 | 54 | def load_yaml(filename): 55 | """ Simply load our yaml file from disk. """ 56 | with open(filename, 'r') as f: 57 | data = yaml.load(f.read(), Loader=yaml.BaseLoader) 58 | 59 | basedir = os.path.dirname(filename) 60 | 61 | try: 62 | validate_yaml(data, basedir) 63 | except: 64 | print("Problem with %r due to..." % filename) 65 | raise 66 | 67 | return data 68 | 69 | 70 | def validate_yaml(data, basedir): 71 | """ Sanity check used to make sure the root question file is valid. """ 72 | assert 'tree' in data 73 | assert 'children' in data['tree'] 74 | validate_tree(data['tree'], basedir) 75 | 76 | 77 | def validate_tree(node, basedir): 78 | """ Sanity check used to make sure the question tree is valid. """ 79 | if not 'children' in node: 80 | if not 'link' in node: 81 | raise ValueError('%r must have either a "href" value or ' 82 | 'a "children" list' % node) 83 | else: 84 | # Handle recursive includes in yaml files. The children of a node 85 | # may be defined in a separate file 86 | if isinstance(node['children'], string_types): 87 | include_file = node['children'] 88 | if not os.path.isabs(include_file): 89 | include_file = os.path.join(basedir, include_file) 90 | 91 | node['children'] = load_yaml(include_file)['tree']['children'] 92 | 93 | # Finally, validate all the children whether they are from a separately 94 | # included file, or not. 95 | for child in node['children']: 96 | validate_tree(child, basedir) 97 | 98 | 99 | def slugify(title, seen): 100 | """ Return a unique id for a node given its title. """ 101 | idx = title.lower() 102 | replacements = { 103 | ' ': '-', 104 | '+': 'plus', 105 | '!': 'exclamation', 106 | ',': 'comma', 107 | '\'': 'apostrophe', 108 | } 109 | for left, right in replacements.items(): 110 | idx = idx.replace(left, right) 111 | while idx in seen: 112 | idx = idx + hashlib.md5(idx.encode('utf-8')).hexdigest()[0] 113 | return idx 114 | 115 | 116 | def prepare_tree(data, node, parent=None, seen=None, _=lambda x: x): 117 | """ Utility method for "enhancing" the data in the question tree. 118 | 119 | This is called typically before rendering the mako template with data. 120 | 121 | A few things happen here: 122 | - Translatable strings are marked up so they can be translated. 123 | - Unique ids are assigned to each node in the tree for use by JS. 124 | - Texts for 'yes', 'no', and 'go back' are assigned at random per node. 125 | - For each node that doesn't have an image defined, propagate the image 126 | defined by its parent node. 127 | 128 | """ 129 | 130 | # Markup strings for translation 131 | if node is data.get('tree'): 132 | for collection in translatable_collections: 133 | if collection in data: 134 | data[collection] = [_(s) for s in data[collection]] 135 | 136 | for field in translatable_fields: 137 | if field in node: 138 | node[field] = _(node[field]) 139 | 140 | # Assign a unique id to this node. 141 | seen = seen or [] 142 | node['id'] = slugify(node.get('title', 'foo'), seen) 143 | seen.append(node['id']) 144 | 145 | # Choose random text for our navigation buttons for this node. 146 | node['affirmative'] = random.choice(data['affirmatives']) 147 | node['negative'] = random.choice(data['negatives']) 148 | node['backlink'] = random.choice(data['backlinks']) 149 | 150 | # Propagate parent images to children unless otherwise specified. 151 | if parent and not 'image' in node and 'image' in parent: 152 | node['image'] = parent['image'] 153 | 154 | # Recursively apply this logic to all children of this node. 155 | for i, child in enumerate(node.get('children', [])): 156 | node['children'][i] = prepare_tree(data, child, parent=node, seen=seen, _=_) 157 | 158 | return node 159 | 160 | 161 | def gather_ids(node): 162 | """ Yields all the unique ids in the question tree recursively. """ 163 | yield node['id'] 164 | for child in node.get('children', []): 165 | for idx in gather_ids(child): 166 | yield idx 167 | 168 | 169 | def produce_graph(tree, dot=None): 170 | """ Given a question tree, returns a pygraphviz object 171 | for later rendering. 172 | """ 173 | import pygraphviz 174 | dot = dot or pygraphviz.AGraph(directed=True) 175 | 176 | idx = tree.get('id', 'root') 177 | dot.add_node(idx, label=tree.get('title', 'Root')) 178 | 179 | for child in tree.get('children', []): 180 | dot = produce_graph(child, dot) 181 | dot.add_edge(idx, child['id']) 182 | 183 | return dot 184 | 185 | 186 | def load_template(filename): 187 | """ Load a mako template and return it for later rendering. """ 188 | return mako.template.Template( 189 | filename=filename, 190 | strict_undefined=True, 191 | output_encoding='utf-8', 192 | ) 193 | 194 | 195 | def translatable_strings(data): 196 | """ A generator that yields tuples containing translatable strings from a 197 | question tree. 198 | 199 | The yielded tuples are of the form (linenumber, string, comment). 200 | """ 201 | for key in translatable_fields: 202 | if key in data: 203 | yield data['__line__'], data[key], key 204 | 205 | for key in translatable_collections: 206 | if key in data: 207 | for string in data[key]: 208 | yield data['__line__'], string, key[:-1] 209 | 210 | for item in data.get('navlinks', []): 211 | yield data['__line__'], item['name'], 'navlink' 212 | 213 | if 'tree' in data: 214 | for items in translatable_strings(data['tree']): 215 | yield items 216 | 217 | children = data.get('children', []) 218 | if isinstance(children, str): 219 | pass 220 | else: 221 | for child in children: 222 | for items in translatable_strings(child): 223 | yield items 224 | 225 | 226 | def load_yaml_with_linenumbers(fileobj): 227 | """ Return yaml with line numbers included in the dict. 228 | 229 | This is similar to our mundane ``load_yaml`` function, except that it 230 | modifies the yaml loader to include line numbers in the data. Our babel 231 | extension which is used to extract translatable strings from our yaml files 232 | uses those line numbers to make things easier on translators. 233 | """ 234 | loader = yaml.Loader(fileobj.read()) 235 | 236 | def compose_node(parent, index): 237 | # the line number where the previous token has ended (plus empty lines) 238 | line = loader.line 239 | node = yaml.composer.Composer.compose_node(loader, parent, index) 240 | node.__line__ = line + 1 241 | return node 242 | 243 | def construct_mapping(node, deep=False): 244 | constructor = yaml.constructor.Constructor.construct_mapping 245 | mapping = constructor(loader, node, deep=deep) 246 | mapping['__line__'] = node.__line__ 247 | return mapping 248 | 249 | loader.compose_node = compose_node 250 | loader.construct_mapping = construct_mapping 251 | return loader.get_single_data() 252 | 253 | 254 | def extract(fileobj, keywords, comment_tags, options): 255 | """ Babel entry-point for extracting translatable strings from our yaml. 256 | 257 | This gets called by 'python setup.py extract_messages' when it encounters a 258 | yaml file. (See setup.py for where we declare the existence of this 259 | function for bable using setuptools 'entry-points'). 260 | """ 261 | data = load_yaml_with_linenumbers(fileobj) 262 | 263 | for lineno, string, comment in translatable_strings(data): 264 | yield lineno, None, [string], [comment] 265 | -------------------------------------------------------------------------------- /static/jquery.uls/js/jquery.uls.languagefilter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jQuery language filter plugin. 3 | * 4 | * Copyright (C) 2012 Alolita Sharma, Amir Aharoni, Arun Ganesh, Brandon Harris, 5 | * Niklas Laxström, Pau Giner, Santhosh Thottingal, Siebrand Mazeland and other 6 | * contributors. See CREDITS for a list. 7 | * 8 | * UniversalLanguageSelector is dual licensed GPLv2 or later and MIT. You don't 9 | * have to do anything special to choose one license or the other and you don't 10 | * have to notify anyone which license you are using. You are free to use 11 | * UniversalLanguageSelector in commercial projects as long as the copyright 12 | * header is left intact. See files GPL-LICENSE and MIT-LICENSE for details. 13 | * 14 | * @file 15 | * @ingroup Extensions 16 | * @licence GNU General Public Licence 2.0 or later 17 | * @licence MIT License 18 | */ 19 | 20 | /** 21 | * Usage: $( 'inputbox' ).languagefilter(); 22 | * The values for autocompletion is from the options.languages. 23 | * The data is in the format of languagecode:languagename. 24 | */ 25 | (function ( $ ) { 26 | 'use strict'; 27 | 28 | var LanguageFilter, delay; 29 | 30 | LanguageFilter = function( element, options ) { 31 | this.$element = $( element ); 32 | this.options = $.extend( {}, $.fn.regionselector.defaults, options ); 33 | this.$element.addClass( 'languagefilter' ); 34 | this.resultCount = 0; 35 | this.$suggestion = this.$element.parents().find( '#' + this.$element.data( 'suggestion' ) ); 36 | this.$clear = this.$element.parents().find( '#' + this.$element.data( 'clear' ) ); 37 | this.selectedLanguage = null; 38 | 39 | this.listen(); 40 | }; 41 | 42 | delay = ( function() { 43 | var timer = 0; 44 | 45 | return function( callback, milliseconds ) { 46 | clearTimeout( timer ); 47 | timer = setTimeout( callback, milliseconds ); 48 | }; 49 | } () ); 50 | 51 | LanguageFilter.prototype = { 52 | listen: function() { 53 | this.$element.on( 'keypress', $.proxy( this.keyup, this ) ) 54 | .on( 'keyup', $.proxy( this.keyup, this ) ); 55 | 56 | if ( this.eventSupported( 'keydown' ) ) { 57 | this.$element.on( 'keydown', $.proxy( this.keyup, this ) ); 58 | } 59 | 60 | if ( this.$clear.length ) { 61 | this.$clear.on( 'click' , $.proxy( this.clear, this ) ); 62 | } 63 | 64 | this.toggleClear(); 65 | }, 66 | 67 | keyup: function( e ) { 68 | var suggestion, query, languageFilter; 69 | 70 | switch( e.keyCode ) { 71 | case 9: // Tab -> Autocomplete 72 | suggestion = this.$suggestion.val(); 73 | 74 | if ( suggestion && suggestion !== this.$element.val() ) { 75 | this.$element.val( suggestion ); 76 | e.preventDefault(); 77 | e.stopPropagation(); 78 | } 79 | break; 80 | case 13: // Enter 81 | if ( !this.options.onSelect ) { 82 | break; 83 | } 84 | 85 | // Avoid bubbling this 'enter' to background page elements 86 | e.preventDefault(); 87 | e.stopPropagation(); 88 | 89 | query = $.trim( this.$element.val() ).toLowerCase(); 90 | 91 | if ( this.selectedLanguage ) { 92 | // this.selectLanguage will be populated from a matching search 93 | this.options.onSelect( this.selectedLanguage ); 94 | } else if ( this.options.languages[query] ) { 95 | // Search is yet to happen (in timeout delay), 96 | // but we have a matching language code. 97 | this.options.onSelect( query ); 98 | } 99 | 100 | break; 101 | default: 102 | languageFilter = this; 103 | 104 | if ( e.which < 32 && 105 | e.which !== 8 // Backspace 106 | ) { 107 | // ignore any ASCII control characters 108 | break; 109 | } 110 | 111 | this.selectedLanguage = null; 112 | 113 | delay( function() { 114 | if ( !languageFilter.$element.val() ) { 115 | languageFilter.clear(); 116 | } else { 117 | languageFilter.options.$target.empty(); 118 | languageFilter.search(); 119 | } 120 | }, 300 ); 121 | 122 | this.toggleClear(); 123 | } 124 | }, 125 | 126 | /** 127 | * Clears the current search removing 128 | * clear buttons and suggestions. 129 | */ 130 | deactivate: function() { 131 | this.$element.val( '' ); 132 | 133 | if ( !$.fn.uls.Constructor.prototype.isMobile() ) { 134 | this.$element.focus(); 135 | } 136 | 137 | this.toggleClear(); 138 | this.autofill(); 139 | }, 140 | 141 | /** 142 | * Clears the search and shows all languages 143 | */ 144 | clear: function() { 145 | this.deactivate(); 146 | this.$element.trigger( 'searchclear.uls' ); 147 | }, 148 | 149 | /** 150 | * Toggles the visibility of clear icon depending 151 | * on whether there is anything to clear. 152 | */ 153 | toggleClear: function() { 154 | if ( !this.$clear.length ) { 155 | return; 156 | } 157 | 158 | if ( this.$element.val() ) { 159 | this.$clear.show(); 160 | } else { 161 | this.$clear.hide(); 162 | } 163 | }, 164 | 165 | search: function() { 166 | var languagesInScript, 167 | query = $.trim( this.$element.val() ), 168 | languages = $.uls.data.getLanguagesByScriptGroup( this.options.languages ), 169 | scriptGroup, langNum, langCode; 170 | 171 | this.resultCount = 0; 172 | 173 | for ( scriptGroup in languages ) { 174 | languagesInScript = languages[scriptGroup]; 175 | 176 | languagesInScript.sort( $.uls.data.sortByAutonym ); 177 | 178 | for ( langNum = 0; langNum < languagesInScript.length; langNum++ ) { 179 | langCode = languagesInScript[langNum]; 180 | 181 | if ( query === '' || this.filter( langCode, query ) ) { 182 | if ( this.resultCount === 0 ) { 183 | // Autofill the first result. 184 | this.autofill( langCode ); 185 | } 186 | 187 | if ( query.toLowerCase() === langCode ) { 188 | this.selectedLanguage = langCode; 189 | } 190 | 191 | if ( this.render( langCode ) ) { 192 | this.resultCount++; 193 | } 194 | } 195 | } 196 | } 197 | 198 | // Also do a search by search API 199 | if( !this.resultCount && this.options.searchAPI && query ) { 200 | this.searchAPI( query ); 201 | } else { 202 | this.resultHandler( query ); 203 | } 204 | }, 205 | 206 | searchAPI: function( query ) { 207 | var languageFilter = this; 208 | 209 | $.get( languageFilter.options.searchAPI, { search: query }, function( result ) { 210 | $.each( result.languagesearch, function( code, name ) { 211 | if ( languageFilter.resultCount === 0 ) { 212 | // Autofill the first result. 213 | languageFilter.autofill( code, name ); 214 | } 215 | 216 | if ( languageFilter.render( code ) ) { 217 | languageFilter.resultCount++; 218 | } 219 | } ); 220 | 221 | languageFilter.resultHandler( query ); 222 | } ); 223 | }, 224 | 225 | /** 226 | * Handler method to be called once search is over. 227 | * Based on search result triggers resultsfound or noresults events 228 | * @param query string 229 | */ 230 | resultHandler: function( query ) { 231 | if ( this.resultCount === 0 ) { 232 | this.$suggestion.val( '' ); 233 | this.$element.trigger( 'noresults.uls', query ); 234 | } else { 235 | this.$element.trigger( 'resultsfound.uls', [query, this.resultCount] ); 236 | } 237 | }, 238 | 239 | autofill: function( langCode, languageName ) { 240 | if ( !this.$suggestion.length ) { 241 | return; 242 | } 243 | 244 | if ( !this.$element.val() ) { 245 | this.$suggestion.val( '' ); 246 | return; 247 | } 248 | 249 | this.selectedLanguage = langCode; 250 | languageName = languageName || this.options.languages[langCode]; 251 | 252 | if ( !languageName ) { 253 | return; 254 | } 255 | 256 | var autonym, 257 | userInput = this.$element.val(), 258 | suggestion = userInput + languageName.substring( userInput.length, languageName.length ); 259 | 260 | if ( suggestion.toLowerCase() !== languageName.toLowerCase() ) { 261 | // see if it was autonym match 262 | autonym = $.uls.data.getAutonym( langCode ) || ''; 263 | suggestion = userInput + autonym.substring( userInput.length, autonym.length ); 264 | 265 | if ( suggestion !== autonym ) { 266 | // Give up. It may be an ISO/script code match. 267 | suggestion = ''; 268 | } 269 | } 270 | 271 | // Make sure that it is a visual prefix. 272 | if ( !isVisualPrefix( userInput, suggestion ) ) { 273 | suggestion = ''; 274 | } 275 | 276 | this.$suggestion.val( suggestion ); 277 | }, 278 | 279 | render: function( langCode ) { 280 | var $target = this.options.$target; 281 | 282 | if ( !$target ) { 283 | return false; 284 | } 285 | 286 | return $target.append( langCode ); 287 | }, 288 | 289 | escapeRegex: function( value ) { 290 | return value.replace( /[\-\[\]{}()*+?.,\\\^$\|#\s]/g, '\\$&' ); 291 | }, 292 | 293 | /** 294 | * A search match happens if any of the following passes: 295 | * a) Language name in current user interface language 296 | * 'starts with' search string. 297 | * b) Language autonym 'starts with' search string. 298 | * c) ISO 639 code match with search string. 299 | * d) ISO 15924 code for the script match the search string. 300 | */ 301 | filter: function( langCode, searchTerm ) { 302 | // FIXME script is ISO 15924 code. We might need actual name of script. 303 | var matcher = new RegExp( '^' + this.escapeRegex( searchTerm ), 'i' ), 304 | languageName = this.options.languages[langCode]; 305 | 306 | return matcher.test( languageName ) || 307 | matcher.test( $.uls.data.getAutonym( langCode ) ) || 308 | matcher.test( langCode ) || 309 | matcher.test( $.uls.data.getScript( langCode ) ); 310 | }, 311 | 312 | eventSupported: function ( eventName ) { 313 | var isSupported = eventName in this.$element; 314 | 315 | if ( !isSupported ) { 316 | this.$element.setAttribute( eventName, 'return;' ); 317 | isSupported = typeof this.$element[eventName] === 'function'; 318 | } 319 | 320 | return isSupported; 321 | } 322 | }; 323 | 324 | $.fn.languagefilter = function( option ) { 325 | return this.each( function() { 326 | var $this = $( this ), 327 | data = $this.data( 'languagefilter' ), 328 | options = typeof option === 'object' && option; 329 | 330 | if ( !data ) { 331 | $this.data( 'languagefilter', ( data = new LanguageFilter( this, options ) ) ); 332 | } 333 | 334 | if ( typeof option === 'string' ) { 335 | data[option](); 336 | } 337 | } ); 338 | }; 339 | 340 | $.fn.languagefilter.defaults = { 341 | $target: null, // Where to append the results 342 | searchAPI: null, 343 | languages: null, // Languages as code:name format. 344 | onSelect: null // Language select handler - like enter in filter textbox. 345 | }; 346 | 347 | $.fn.languagefilter.Constructor = LanguageFilter; 348 | 349 | /** 350 | * Check if a prefix is visually prefix of a string 351 | * @param prefix string 352 | * @param string string 353 | */ 354 | function isVisualPrefix( prefix, string ) { 355 | // Pre-base vowel signs of Indic languages. A vowel sign is called pre-base if 356 | // consonant + vowel becomes [vowel][consonant] when rendered. Eg: ക + െ => കെ 357 | var prebases = 'െേൈൊോൌெேைொோௌେୈୋୌિਿिিেৈোৌෙේෛොෝෞ'; 358 | return prebases.indexOf( string[prefix.length] ) <= 0; 359 | } 360 | } ( jQuery ) ); 361 | -------------------------------------------------------------------------------- /static/jquery.uls/js/jquery.uls.lcd.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Universal Language Selector 3 | * Language category display component - Used for showing the search results, 4 | * grouped by regions, scripts 5 | * 6 | * Copyright (C) 2012 Alolita Sharma, Amir Aharoni, Arun Ganesh, Brandon Harris, 7 | * Niklas Laxström, Pau Giner, Santhosh Thottingal, Siebrand Mazeland and other 8 | * contributors. See CREDITS for a list. 9 | * 10 | * UniversalLanguageSelector is dual licensed GPLv2 or later and MIT. You don't 11 | * have to do anything special to choose one license or the other and you don't 12 | * have to notify anyone which license you are using. You are free to use 13 | * UniversalLanguageSelector in commercial projects as long as the copyright 14 | * header is left intact. See files GPL-LICENSE and MIT-LICENSE for details. 15 | * 16 | * @file 17 | * @ingroup Extensions 18 | * @licence GNU General Public Licence 2.0 or later 19 | * @licence MIT License 20 | */ 21 | 22 | ( function ( $ ) { 23 | 'use strict'; 24 | 25 | var noResultsTemplate, LanguageCategoryDisplay; 26 | 27 | /*jshint multistr:true */ 28 | noResultsTemplate = '
\ 29 |

\ 30 | No results found\ 31 |

\ 32 |
\ 33 |
\ 34 |

\ 35 | You can search by language name, \ 36 | script name, ISO code of language or \ 37 | you can browse by region:\ 38 | America, \ 39 | Europe, \ 40 | Middle East, \ 41 | Africa, \ 42 | Asia, \ 43 | Pacific, \ 44 | Worldwide.\ 45 |

\ 46 |
\ 47 |
\ 48 |
'; 49 | /*jshint multistr:false */ 50 | 51 | LanguageCategoryDisplay = function ( element, options ) { 52 | this.$element = $( element ); 53 | this.options = $.extend( {}, $.fn.lcd.defaults, options ); 54 | this.$element.addClass( 'lcd' ); 55 | this.regionLanguages = {}; 56 | this.renderTimeout = null; 57 | this.cachedQuicklist = null; 58 | 59 | this.$element.append( $( noResultsTemplate ) ); 60 | this.$noResults = this.$element.children( '.uls-no-results-view' ); 61 | 62 | this.render(); 63 | this.listen(); 64 | }; 65 | 66 | LanguageCategoryDisplay.prototype = { 67 | constructor: LanguageCategoryDisplay, 68 | 69 | /** 70 | * Adds language to the language list. 71 | * @param {string} langCode 72 | * @param {string} [regionCode] 73 | * @return {bool} Whether the language was added. 74 | */ 75 | append: function ( langCode, regionCode ) { 76 | var lcd = this, i, regions; 77 | 78 | if ( !this.options.languages[langCode] ) { 79 | // Language is unknown or not in the list of languages for this context. 80 | return false; 81 | } 82 | 83 | if ( regionCode ) { 84 | regions = [regionCode]; 85 | } else { 86 | regions = $.uls.data.getRegions( langCode ); 87 | } 88 | 89 | // Worldwides only displayed once 90 | if ( $.inArray( 'WW', regions ) > -1 ) { 91 | regions = ['WW']; 92 | } 93 | 94 | for ( i = 0; i < regions.length; i++ ) { 95 | this.regionLanguages[regions[i]].push( langCode ); 96 | } 97 | 98 | // Work around the bad interface, delay rendering until we have got 99 | // all the languages to speed up performance. 100 | window.clearTimeout( this.renderTimeout ); 101 | this.renderTimeout = window.setTimeout( function () { 102 | lcd.renderRegions(); 103 | }, 50 ); 104 | 105 | return true; 106 | }, 107 | 108 | render: function () { 109 | var $section, 110 | lcd = this, 111 | regions = [], 112 | regionNames = { 113 | // These are fallback text when i18n library not present 114 | WW: 'Worldwide', 115 | SP: 'Special', 116 | AM: 'America', 117 | EU: 'Europe', 118 | ME: 'Middle East', 119 | AS: 'Asia', 120 | AF: 'Africa', 121 | PA: 'Pacific' 122 | }; 123 | 124 | regions.push( this.buildQuicklist() ); 125 | 126 | $.each( $.uls.data.regiongroups, function ( regionCode ) { 127 | lcd.regionLanguages[regionCode] = []; 128 | // Don't show the region unless it was enabled 129 | if ( $.inArray( regionCode, lcd.options.showRegions ) === -1 ) { 130 | return; 131 | } 132 | 133 | $section = $( '
' ) 134 | .addClass( 'eleven columns offset-by-one uls-lcd-region-section hide' ) 135 | .attr( 'id', regionCode ) 136 | .append( 137 | $( '

' ) 138 | .attr( 'data-i18n', 'uls-region-' + regionCode ) 139 | .addClass( 'eleven columns uls-lcd-region-title' ) 140 | .text( regionNames[regionCode] ) 141 | ); 142 | 143 | regions.push( $section ); 144 | } ); 145 | 146 | lcd.$element.append( regions ); 147 | 148 | this.i18n(); 149 | }, 150 | 151 | /** 152 | * Renders a region and displays it if it has content. 153 | */ 154 | renderRegions: function () { 155 | var lcd = this, languages, 156 | items = lcd.options.itemsPerColumn, 157 | columns = 4; 158 | 159 | this.$noResults.addClass( 'hide' ); 160 | this.$element.find( '.uls-lcd-region-section' ).each( function () { 161 | var $region = $( this ), 162 | regionCode = $region.attr( 'id' ); 163 | 164 | if ( $region.is( '#uls-lcd-quicklist' ) ) { 165 | return; 166 | } 167 | 168 | $region.children( '.uls-language-block' ).remove(); 169 | 170 | languages = lcd.regionLanguages[regionCode]; 171 | if ( !languages || languages.length === 0 ) { 172 | $region.addClass( 'hide' ); 173 | return; 174 | } 175 | 176 | lcd.renderRegion( $region, languages, items, columns ); 177 | $region.removeClass( 'hide' ); 178 | 179 | lcd.regionLanguages[regionCode] = []; 180 | } ); 181 | 182 | }, 183 | 184 | /** 185 | * Adds given languages sorted into rows and columns into given element. 186 | * @param {jQuery} $region Element to add language list. 187 | * @param {array} languages List of language codes. 188 | * @param {number} itemsPerColumn How many languages fit in a column. 189 | * @param {number} columnsPerRow How many columns fit in a row. 190 | */ 191 | renderRegion: function( $region, languages, itemsPerColumn, columnsPerRow ) { 192 | var i, lastItem, currentScript, nextScript, force, 193 | len = languages.length, 194 | items = [], 195 | columns = [], 196 | rows = []; 197 | 198 | for ( i = 0; i < len; i++ ) { 199 | force = false; 200 | nextScript = $.uls.data.getScriptGroupOfLanguage( languages[i+1] ); 201 | 202 | lastItem = len - i === 1; 203 | // Force column break if script changes and column has more than one row already 204 | if ( i === 0 ) { 205 | currentScript = $.uls.data.getScriptGroupOfLanguage( languages[i] ); 206 | } else if ( currentScript !== nextScript && items.length > 1 ) { 207 | force = true; 208 | } 209 | currentScript = nextScript; 210 | 211 | items.push( this.renderItem( languages[i] ) ); 212 | 213 | if ( items.length >= itemsPerColumn || lastItem || force ) { 214 | columns.push( $( '