diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0f15a6a --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea +_site +.DS_Store \ No newline at end of file diff --git a/CNAME b/CNAME new file mode 100644 index 0000000..ad12dcf --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +101.opensuse.org diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..6202e7e --- /dev/null +++ b/Gemfile @@ -0,0 +1,4 @@ +source 'https://rubygems.org' + +gem 'jekyll' +gem 'jekyll-redirect-from' diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..0ed0c5f --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,42 @@ +GEM + remote: https://rubygems.org/ + specs: + colorator (0.1) + ffi (1.9.10) + jekyll (3.1.3) + colorator (~> 0.1) + jekyll-sass-converter (~> 1.0) + jekyll-watch (~> 1.1) + kramdown (~> 1.3) + liquid (~> 3.0) + mercenary (~> 0.3.3) + rouge (~> 1.7) + safe_yaml (~> 1.0) + jekyll-redirect-from (0.10.0) + jekyll (>= 2.0) + jekyll-sass-converter (1.4.0) + sass (~> 3.4) + jekyll-watch (1.3.1) + listen (~> 3.0) + kramdown (1.10.0) + liquid (3.0.6) + listen (3.1.1) + rb-fsevent (>= 0.9.3) + rb-inotify (>= 0.9.7) + mercenary (0.3.6) + rb-fsevent (0.9.7) + rb-inotify (0.9.7) + ffi (>= 0.5.0) + rouge (1.10.1) + safe_yaml (1.0.4) + sass (3.4.22) + +PLATFORMS + ruby + +DEPENDENCIES + jekyll + jekyll-redirect-from + +BUNDLED WITH + 1.11.2 diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..d8374a2 --- /dev/null +++ b/ISSUE_TEMPLATE.md @@ -0,0 +1,13 @@ +**Project Title:** Project title, short enough to catch attention + +**Description:** General information about the project, avoid one Liners, the description should be as detailed as possible. + +**Deliverable:** Expectations from the student at the end of the project + +**Mentor:** Who is the mentor? Who is the Co-Mentor? Also please assign the issue to the mentor! + +**Skills:** Which skills are needed? Programming languages, frameworks, concepts etc. + +**Skill Level:** Easy, Medium, Hard + +**Get started:** Tasks that mentors may want to suggest students so that they can start contributing to the code base (e.g. junior jobs, low hanging fruits, discussion on the mailing list) diff --git a/README.md b/README.md index 1f5cf85..3116349 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,13 @@ -# GSoC-Ideas -Testing. -To discuss ideas and potential projects for Google Summer of Code +# CloudCV GSoC Ideas Page + +## Development +This site is built with [Jekyll](https://github.com/jekyll/jekyll) and [Bootstrap](https://github.com/twbs/bootstrap). + +### Adding new project +1. Copy a file project file from the ``_posts`` folder and rename it to ``{date}-project-{project-name}`` +2. Fill the fields in the copied file accordingly +3. Run ``jekyll serve`` and check in the browser on ``0.0.0.0:4000`` if the site looks like expected +4. Send a Pull Request + +## Acknowledgements +Based on the OpenSUSE theme. \ No newline at end of file diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..087ae95 --- /dev/null +++ b/_config.yml @@ -0,0 +1,24 @@ +# ----------------------- # +# Main Configs # +# ----------------------- # + +title: CloudCV +subTitle: +email: virajp@vt.edu +author: Viraj Prabhu +description: The CloudCV mentoring project collects ideas for Google Summer of Code. +copyright: 'Copyright © 2017 CloudCV. All Rights Reserved.' +github_organization: Cloud-CV +github_repository: GSoC-Ideas +gems: + - jekyll-redirect-from +# ----------------------- # +# Jekyll & Plugins # +# ----------------------- # + +# Build settings +markdown: kramdown +permalink: pretty + +collections: +- gsoc_current diff --git a/_gsoc_current/2016-04-21-Ticket-payment-feature-for-OSEM.markdown b/_gsoc_current/2016-04-21-Ticket-payment-feature-for-OSEM.markdown new file mode 100644 index 0000000..7ac57e6 --- /dev/null +++ b/_gsoc_current/2016-04-21-Ticket-payment-feature-for-OSEM.markdown @@ -0,0 +1,16 @@ +--- +img: osem.png +mentors: + - name: Artem Chernikov + github: kalabiyau + - name: Hernan Schmidt + github: lagartoflojo +mentees: + - name: Rishabh Saxena + github: rishabhs95 +website: https://news.opensuse.org/category/project/gsoc-project/ +--- +The [Open Source Event Manager](http://osem.io/) currently lacks the ability for +a user to pay for conference tickets online. In this project, we aim to fill +this gap in functionality by adding ticket payment features. This will improve +user experience by making the ticket purchase workflow smoother. diff --git a/_gsoc_current/2016-04-22-Alternatives-YaST-Module.markdown b/_gsoc_current/2016-04-22-Alternatives-YaST-Module.markdown new file mode 100644 index 0000000..4449da2 --- /dev/null +++ b/_gsoc_current/2016-04-22-Alternatives-YaST-Module.markdown @@ -0,0 +1,15 @@ +--- +img: yast.png +mentors: + - name: Ancor Gonzalez Sosa + github: ancorgs + - name: Josef Reidinger + github: jreidinger +mentees: + - name: Joaquín Yeray Martín de la Nuez + github: jyeray +website: https://news.opensuse.org/category/project/gsoc-project/ +--- +Build a [YaST](http://yast.github.io/) module to manage update-alternatives with an +easy and simple graphical user interface that makes easier the configuration of +alternatives and enhance the user experience. diff --git a/_gsoc_current/2016-04-22-Improve-One-Click-Installer.markdown b/_gsoc_current/2016-04-22-Improve-One-Click-Installer.markdown new file mode 100644 index 0000000..b18b1c0 --- /dev/null +++ b/_gsoc_current/2016-04-22-Improve-One-Click-Installer.markdown @@ -0,0 +1,15 @@ +--- +img: openSUSE.png +mentors: + - name: Antonio Larrosa + github: antlarr + - name: Cornelius Schumacher + github: cornelius +mentees: + - name: Shalom Rachapudi David + github: shalomRachapudi +website: https://news.opensuse.org/category/project/gsoc-project/ +--- +This project aims to create a version of the QT based [one-click installer](https://github.com/openSUSE/one-click-installer) +with much improved user interface which is easier to use, gives more information +and looks nicer than the current version. diff --git a/_gsoc_current/2016-04-22-Improve-the-UI-of-Portus.markdown b/_gsoc_current/2016-04-22-Improve-the-UI-of-Portus.markdown new file mode 100644 index 0000000..ce47d21 --- /dev/null +++ b/_gsoc_current/2016-04-22-Improve-the-UI-of-Portus.markdown @@ -0,0 +1,14 @@ +--- +img: portus.png +mentors: + - name: Miquel Sabaté + github: mssola +mentees: + - name: Matheus Fernandes + github: msfernandes +website: https://news.opensuse.org/category/project/gsoc-project/ +--- +This project concentrates on the user interface (UI) of +[Portus](https://github.com/SUSE/Portus). It will address layout problems +on different devices, especially on small screens. Furthermore the UI also need +some improvements in performance, filtering and search. diff --git a/_gsoc_current/2016-04-22-Port-Jangouts-from-AngularJS-1.4.markdown b/_gsoc_current/2016-04-22-Port-Jangouts-from-AngularJS-1.4.markdown new file mode 100644 index 0000000..34a9363 --- /dev/null +++ b/_gsoc_current/2016-04-22-Port-Jangouts-from-AngularJS-1.4.markdown @@ -0,0 +1,14 @@ +--- +img: jangouts.png +mentors: + - name: Imobach González Sosa + github: imobach + - name: Ancor González Sosa + github: ancorgs +mentees: + - name: Martin Garcia + github: magarcia +website: https://news.opensuse.org/category/project/gsoc-project/ +--- +Migrate the code of [Jangouts](https://github.com/jangouts/jangouts) +from Angular 1.4 to Angular 2. During the transition a test suite will be added. diff --git a/_gsoc_current/2016-04-22-enhance-OSEM-visitor-experience.markdown b/_gsoc_current/2016-04-22-enhance-OSEM-visitor-experience.markdown new file mode 100644 index 0000000..d90eff7 --- /dev/null +++ b/_gsoc_current/2016-04-22-enhance-OSEM-visitor-experience.markdown @@ -0,0 +1,16 @@ +--- +img: osem.png +mentors: + - name: Christian Bruckmayer + github: chrisbr + - name: Henne Vogelsang + github: hennevogel +mentees: + - name: Ana María Martínez + github: Ana06 +website: https://news.opensuse.org/category/project/gsoc-project/ +--- +This project concentrates on the schedule of Events in the [Open Source Event Manager](http://osem.io/). +The admin interface to build the schedule will be integrated, the scheudile page +will be made mobile friendly. It will introduce schedule versions and allowing +individual user schedules. diff --git a/_includes/footer.html b/_includes/footer.html new file mode 100644 index 0000000..3b31308 --- /dev/null +++ b/_includes/footer.html @@ -0,0 +1,32 @@ + + diff --git a/_includes/gsoc-accepted-projects.html b/_includes/gsoc-accepted-projects.html new file mode 100644 index 0000000..2875c2c --- /dev/null +++ b/_includes/gsoc-accepted-projects.html @@ -0,0 +1,19 @@ +
+
+
+
+

Google Summer of Code 2016 Projects

+
+ {% for project in site.gsoc_current limit:3 %} + {% include project.html %} + {% endfor %} +
+
+ {% for project in site.gsoc_current offset:3 %} + {% include project.html %} + {% endfor %} +
+
+
+
+
diff --git a/_includes/gsoc-howto.html b/_includes/gsoc-howto.html new file mode 100644 index 0000000..c80e91d --- /dev/null +++ b/_includes/gsoc-howto.html @@ -0,0 +1,47 @@ +
+
+
+
+
+
+

How does it work?

+
+

+ + Check if you're eligible for GSoC on the official Google Summer of Code website! +

+

+ + Select one of our mentoring projects +

+

+ + Get in touch with the mentor, follow the get started instructions in the project description. +

+

+ + If the mentor is to your liking and you want to do this project for GSoC you write up a + project proposal. +

+

+ + Between 14-25 March 2016 you have to submit your project proposal to the + GSoC program site. +

+

+ + On April 22 2016 Google will announce accepted student proposals +

+

+ + Code the summer away. +

+

+ + SUCCESS! +

+
+
+
+
+
diff --git a/_includes/gsoc-mentor_process.html b/_includes/gsoc-mentor_process.html new file mode 100644 index 0000000..7e816a7 --- /dev/null +++ b/_includes/gsoc-mentor_process.html @@ -0,0 +1,68 @@ +
+
+
+
+
+
+

Okay, let's mentor!

+

+ + + + + Add issues for your project to our repository +

+

+ + + + + Label your issues with GSoC and assign yourself +

+

+ + + + + When students get in touch with you in the issue, help them +

+

+ + + + + Between 14-25 March 2016 students have to submit a + proposal + for your project to the + GSoC program site. + Help them +

+

+ + + + + On April 22 2016 Google will announce accepted student proposals +

+

+ + + + + If your one of your students proposals makes the cut GSoC starts + and runs all + summer! +

+

+ Google Summer of Code is a very well thought out program with many + roles, processes and terms. Still have questions? Check the + FAQ + and/or get in touch with our team! +

+
+
+ +
+
+
+
diff --git a/_includes/gsoc-projects.html b/_includes/gsoc-projects.html new file mode 100644 index 0000000..abf824c --- /dev/null +++ b/_includes/gsoc-projects.html @@ -0,0 +1,32 @@ +
+
+
+
+
+
+
+

What can I do?

+

Just pick one of our projects and follow the get started instructions:

+
+

Please wait for loading the projects ...

+ + + + + + + + + + +

You can find a description of the different openSUSE projects on our 101 projects page.

+

+ Nothing you like? Do you have your own idea? + Awesome, just get in touch with our team! + Creativity and initiative are qualities we highly appreciate. +

+
+
+
+
+
diff --git a/_includes/gsoc-summary.html b/_includes/gsoc-summary.html new file mode 100644 index 0000000..87eb84c --- /dev/null +++ b/_includes/gsoc-summary.html @@ -0,0 +1,68 @@ +
+
+
+
+
+
+

Successful projects

+
+
+ {% for post in site.posts reversed %} + {% if post.type == 'gsoc' %} +
+ +
+
+ +
+
+ +
+
+

{{ post.title }}

+

+
+
+ {% endif %} + {% endfor %} +
+

+ This is just a small selection of projects, you can find all of them on Google Melange. +

+
+
+
+
+
+ + +{% for post in site.posts %} + {% if post.type == 'gsoc' %} + + {% endif %} +{% endfor %} diff --git a/_includes/head.html b/_includes/head.html new file mode 100644 index 0000000..883e578 --- /dev/null +++ b/_includes/head.html @@ -0,0 +1,46 @@ + + + + + + + + + {% if page.title %}{{ page.title }}{% else %}{{ site.title }}{% endif %} + + + + + + + + + + + + + + + + + + + + + diff --git a/_includes/header.html b/_includes/header.html new file mode 100644 index 0000000..5c08599 --- /dev/null +++ b/_includes/header.html @@ -0,0 +1,24 @@ +
+
+
+
+
+
+

{{page.title}}

+
+ +
+
+
+
+ +
+ +
diff --git a/_includes/js.html b/_includes/js.html new file mode 100644 index 0000000..752852d --- /dev/null +++ b/_includes/js.html @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/_includes/mentee-guidelines.html b/_includes/mentee-guidelines.html new file mode 100644 index 0000000..1875ae3 --- /dev/null +++ b/_includes/mentee-guidelines.html @@ -0,0 +1,67 @@ +

+ You want to know what it means to be mentored by us? Wonderful! Here + are the five things that are important for you to know/do to become + a Free Software hacker! +

+ +

1. Understand the goal

+

+ The goal of all of this is to guide you on becoming a regular contributor + to Free Software. It's as much about building relationships, learning + how we communicate and collaborate as it is about hacking code + and using tools. This is about you becoming a full-fledged contributor + to openSUSE! +

+

+ That also means that the goal of this program is not to teach you the + basics of software development. Those skills can best be required + elsewhere + and we expect you to have them. +

+

2. Help us to get to know you

+

+ We are here for you to talk about every + aspect of this process. Introduce yourself on our + mailing list + ("Hey I'm thinking about becoming a mentee in 101 and I'm not + sure about XXX and have questions about YYY"), chat with us on + IRC and if you're + already sure about your project, introduce yourself to the mentor directly. + And even if you're not sure that you want to do this at all, come talk + to us and we will find out together if this is for you or not. +

+

3. Be courageous and committed

+

+ Find a project that challenges and that exites you. But think about your + choice carefully, you're going to be doing it for a while. And while + you are at it we expect you to be self-motivated and proactive. This + will work out best if you choose the right thing to do, if you build a + relationship with your mentor, set realistic goals and if you're willing + to respond to change. +

+

4. Communicate

+

+ In our community people with different levels of expertise, + speaking different languages and having different cultural backgrounds + openly share their ideas. That's why communication is king. We expect + you to communicate daily with your mentor, to write short reports at least + once a week and we expect that you tell us if there is something you + can't do. Share what you are up to, share your challenges and share + what you have learned. Open, honest and prompt communication is the key + to success! +

+

5. Collaborate

+

+ Make your code readable and understandable by everyone else in the project. + Document code that isn't obvious. Commit your code early, commit often. + Get your code reviewed and review code of others as often as you can. + Make sure everyone can use, share, study, modify, and share + modified versions of what you produce. +

+

+ Accept that there are different ways to work and different goals for + people. Respect people and their contributions, respect other opinions + and beliefs. Listen to arguments and address problems in a constructive + and open way. A diverse community based on mutual respect is the base + for a creative and productive environment. +

diff --git a/_includes/mentor-guidelines.html b/_includes/mentor-guidelines.html new file mode 100644 index 0000000..929bcef --- /dev/null +++ b/_includes/mentor-guidelines.html @@ -0,0 +1,122 @@ +

+ You want to know what it means to mentor a new contributor to our project? + Wonderful, let us guide you on what we think is important to know, what + our best practices are and which behavior in mentors we desire. + Here is our ten step program for mentoring! +

+ + +

1. Mentees First

+

+ We believe in putting the mentees needs first. You need to emphasize + with the mentee, understand his personality, requirements and + circumstances. Make an effort to understand where the mentee comes from + and what you need to do, to get them to contribute to free software. + Mentoring is about them, not about your or CloudCVs needs. +

+

2. Build a Relationship and have a lot of fun...

+

+ The most important goal is that you build a relationship based on trust + and respect with the mentee and that you have fun together. Without this + everything else you'll do is in vein! Such relationships do not just + happen, they need ongoing effort and attention, especially during the + early stages. +

+

3. Be a role model

+

+ Today this sounds like a weird thing from the 60s and it makes most + people uncomfortable but you need to be a role model for your mentee! + Free Software development is as much about behavior in a community, + knowing what you want, and the ability to convey this to others as it + is about pointers, gcc macros and git command line options. You need + to ensure that your mentee can follow your example and that you're + not passing on your bad habits. You are responsible for developing + the competence AND character of the mentee! +

+

4. Set goals and expectations

+

+ The first thing you'll do together with the mentee is to prepare for the + far future. Precise, reasonable and well-thought-out goals are a must + for every successful mentoring project. Getting into free software + development can be an overwhelming and daunting endeavor. A decent + plan provides a path and helps to take one step after the other into + this strange new world. It's your duty that the goals and expectations + for the project are do-able, provide orientation and are technically sound. + You're the expert! +

+

5. Track Progress

+

+ We encourage you to set up regular milestones in your plan that get + reviewed and celebrated by you and your mentee. It's important to + understand where you are and how you progress. Additionally we believe + that talking as often as you can is what builds the relationship and + bonding with your mentee. The consistency of your relationship is very + important. Find a regular schedule where you meet, we recommend short + daily meetings to check in on progress and address obstacles. +

+

6. Use the community

+

+ Our community consists of very experienced people and is + one of the biggest and diverse free software projects on this planet. + We provide well thought out structures, processes and infrastructure + that reflect our experience with introducing new contributors since + day one of our existence. CloudCV believes that one of the most + important tasks is to bring in new hackers with new perspectives, + backgrounds and ideas into our community. Make sure that you make use + of this environment to the full extent! +

+

7. Be Agile

+

+ The only thing that is constant is change! You need to adapt your plan, + milestones, time line, the technical details, your behavior and + attitude during your time. Respond to change, help your mentee to do + the same. Be open, courageous, and committed! +

+

8. Collaborate

+

+ Whatever challenge you face, remember you are not alone! You mingle on + the shoulders of giants together with many other CloudCV mentors. + Make sure you share your experience, your ideas and your problems with + them. All of you strive to reach the same goal: getting people into + free software. Make sure you work together in an open, transparent and + friendly manner as part of our worldwide community. +

+

9. Want this

+

+ Mentoring requires time, effort and endurance. It will cost you some + evening you wanted to go out with your friends, at least an hour every + day and a lot of nerves because you explain things that are obvious to + you over and over again. If you are not sure that you are willing to do + this then it's probably better you don't do it. +

+

+ On the other hand mentoring will give you the satisfaction of being a + positive influence in the life of someone else. You will see a new + contributor grow into our community and you will achieve results + that matter for CloudCV. You will also get the opportunity to learn + something together and sometimes even making a new friend for life! +

+

10. Don't follow this program, form your own opinion!

+

+ This HOWTO is no religion and there are many awesome resources about + mentoring in general and about mentoring for free software development + in particular out there. Don't trust us, inform yourself and make up + your own opinion! Here are some pointers. +

+ diff --git a/_includes/mentor.html b/_includes/mentor.html new file mode 100644 index 0000000..0489004 --- /dev/null +++ b/_includes/mentor.html @@ -0,0 +1,31 @@ +
+
+
+
+

Become a Mentor!

+

+ The world needs more free software hackers/code. + We think mentoring is very important for the future of CloudCV. + It brings new people to the culture and practices of free software + who will enrich our community with their skillsets, ideas and talents. +

+

+ Being a mentor requires your time, a clear vision of your + project and good organization skills. If you think you would be + a good fit to mentor of our projects, do reach out to us! + +

+
+
+
diff --git a/_includes/mentor_process.html b/_includes/mentor_process.html new file mode 100644 index 0000000..7a284b3 --- /dev/null +++ b/_includes/mentor_process.html @@ -0,0 +1,36 @@ +
+
+
+
+
+
+

Okay, let's mentor!

+

+ + + + + Contact + us if you have relevant experience and want to get involved! +

+

+ + + + + Add issues for your project to our repository +

+

+ + + + + Label your issues with your projects name and assign yourself +

+
+
+ +
+
+
+
diff --git a/_includes/more-mentee.html b/_includes/more-mentee.html new file mode 100644 index 0000000..56c726d --- /dev/null +++ b/_includes/more-mentee.html @@ -0,0 +1,13 @@ +
+

+ Still not convinced this is right for you? Check out a + . +

+
+ {% include mentee-guidelines.html %} +
+
diff --git a/_includes/navigation.html b/_includes/navigation.html new file mode 100644 index 0000000..a5f8825 --- /dev/null +++ b/_includes/navigation.html @@ -0,0 +1,34 @@ + + diff --git a/_includes/project.html b/_includes/project.html new file mode 100644 index 0000000..02726ed --- /dev/null +++ b/_includes/project.html @@ -0,0 +1,32 @@ +
+ +
+

{{ project.title }}

+

+ {{ project.content }} +

+
+
Mentor:
+
+ {% for mentor in project.mentors %} + {% if mentor.github %} + {{mentor.name}} + {% else %} + {{mentor.name}} + {% endif %} + {% unless forloop.last %}, {% endunless %} + {% endfor %} +
+
Mentee:
+
+ {% for mentee in project.mentees %} + {% if mentee.github %} + {{ mentee.name }} + {% else %} + {{mentee.name}} + {% endif %} + {% endfor %} +
+
+
+
diff --git a/_includes/teaser.md b/_includes/teaser.md new file mode 100644 index 0000000..b95f2dc --- /dev/null +++ b/_includes/teaser.md @@ -0,0 +1,6 @@ +CloudCV began in the summer of 2014 as a research project within the Machine Learning and Perception lab at Virginia Tech, with the ambitious goal of democratizing computer vision and machine learning. We’re a young community working towards enabling developers, researchers, and fellow students to leverage Artificial Intelligence technology as a service and to share state of the art algorithms with the research community. +

+ +We have participated in the past two installments of Google Summer of Code, over the course of which our students built several excellent tools and features. If you are interested in participating as a student or mentor, scroll down to check out our projects and get involved! Check out our blog to read our contribution guidelines or to know more about past GSoC's. + +If you still have questions, feel free to reach out to us on our Gitter channel or on our mailing list.

diff --git a/_layouts/default.html b/_layouts/default.html new file mode 100644 index 0000000..5bcb885 --- /dev/null +++ b/_layouts/default.html @@ -0,0 +1,12 @@ + + + {% include head.html %} + + {% include navigation.html %} + + {{ content }} + + {% include footer.html %} + {% include js.html %} + + diff --git a/_posts/2015-12-10-project-evalai.markdown b/_posts/2015-12-10-project-evalai.markdown new file mode 100644 index 0000000..6c6963b --- /dev/null +++ b/_posts/2015-12-10-project-evalai.markdown @@ -0,0 +1,11 @@ +--- +layout: default +img: evalai.png +title: EvalAI +type: project +technologies: [Django, Angular, RabbitMQ] +website: +github: https://github.com/Cloud-CV/EvalAI +description: | +--- +EvalAI is a platform for hosting AI Challenges. diff --git a/_posts/2015-12-11-project-cvfy.markdown b/_posts/2015-12-11-project-cvfy.markdown new file mode 100644 index 0000000..b0ff30c --- /dev/null +++ b/_posts/2015-12-11-project-cvfy.markdown @@ -0,0 +1,12 @@ +--- +layout: default +img: cvfy.png +title: CloudCVfy +type: project +technologies: [ReactJS, Docker] +website: +github: https://github.com/Cloud-CV/cvfy-frontend +description: | +--- + +CloudCVfy is a project to automatically build an interactive web-based demo for your machine learning model or library. \ No newline at end of file diff --git a/_posts/2015-12-12-project-ide.markdown b/_posts/2015-12-12-project-ide.markdown new file mode 100644 index 0000000..b24adb4 --- /dev/null +++ b/_posts/2015-12-12-project-ide.markdown @@ -0,0 +1,11 @@ +--- +layout: default +img: ide.png +title: IDE +type: project +technologies: [React, Django] +website: +github: https://github.com/Cloud-CV/IDE +description: | +--- +IDE provides a simple drag and drop interface to collaboratively build and configure deep neural networks online. \ No newline at end of file diff --git a/_posts/cloucv.markdown b/_posts/cloucv.markdown new file mode 100644 index 0000000..8a5ff29 --- /dev/null +++ b/_posts/cloucv.markdown @@ -0,0 +1,11 @@ +--- +layout: default +img: machinery.png +title: CloudCV +type: project +technologies: [Django, JavaScript] +website: http://cloudcv.org +github: https://github.com/Cloud-CV/CloudCV +description: | +--- +CloudCV - Large-Scale Distributed Computer Vision As A Cloud Service diff --git a/css/landing-page.css b/css/landing-page.css new file mode 100644 index 0000000..f5bdfba --- /dev/null +++ b/css/landing-page.css @@ -0,0 +1,336 @@ +/*! + * Start Bootstrap - Landing Page Bootstrap Theme (http://startbootstrap.com) + * Code licensed under the Apache License v2.0. + * For details, see http://www.apache.org/licenses/LICENSE-2.0. + */ + +body, +html { + width: 100%; + height: 100%; +} + +body, +h1, +h2, +h3, +h4, +h5, +h6 { + font-family: "Lato","Helvetica Neue",Helvetica,Arial,sans-serif; + font-weight: 700; +} + +.lead { + font-size: 18px; + font-weight: 400; +} + +.intro-header { + padding-top: 50px; /* If you're making other pages, make sure there is 50px of padding to make sure the navbar doesn't overlap content! */ + padding-bottom: 50px; + text-align: center; + color: #f8f8f8; + background: url(../img/intro-bg.jpg) no-repeat center center; + background-size: cover; +} + +.intro-header-gsoc { + background: url(../img/gsoc/intro-bg.jpg) no-repeat center center; + background-size: cover; + color: #73ba25; +} + +.intro-header-mentor { + background: url(../img/mentor-intro-bg.jpg) no-repeat center center; + background-size: cover; + color: #73ba25; +} + +.intro-message { + position: relative; + padding-top: 20%; + padding-bottom: 20%; +} + +.intro-message > h1 { + margin: 0; + text-shadow: 2px 2px 3px rgba(0,0,0,0.6); + font-size: 5em; +} + +.intro-divider { + width: 400px; + border-top: 1px solid #f8f8f8; + border-bottom: 1px solid rgba(0,0,0,0.2); +} + +.intro-message > h3 { + text-shadow: 2px 2px 3px rgba(0,0,0,0.6); +} + +@media(max-width:767px) { + .intro-message { + padding-bottom: 15%; + } + + .intro-message > h1 { + font-size: 3em; + } + + ul.intro-social-buttons > li { + display: block; + margin-bottom: 20px; + padding: 0; + } + + ul.intro-social-buttons > li:last-child { + margin-bottom: 0; + } + + .intro-divider { + width: 100%; + } +} + +.network-name { + text-transform: uppercase; + font-size: 14px; + font-weight: 400; + letter-spacing: 2px; +} + +#about, #mentor { + padding: 40px 0 20px; +} + +section { + padding: 20px 0 20px; +} +section:nth-of-type(odd) { + background: #f8f8f8; + border-top: 1px solid #e7e7e7; + border-bottom: 1px solid #e7e7e7; +} +section:first-of-type { + padding: 20px 0 0; +} + +.section-heading { + margin-bottom: 30px; +} + +.section-heading-spacer { + float: left; + width: 200px; + border-top: 3px solid #e7e7e7; +} + +.banner { + padding: 100px 0; + color: #f8f8f8; + background: url(../img/banner-bg.jpg) no-repeat center center; + background-size: cover; +} + +.banner h2 { + margin: 0; + text-shadow: 2px 2px 3px rgba(0,0,0,0.6); + font-size: 3em; +} + +.banner ul { + margin-bottom: 0; +} + +.banner-social-buttons { + float: right; + margin-top: 0; +} + +@media(max-width:1199px) { + ul.banner-social-buttons { + float: left; + margin-top: 15px; + } +} + +@media(max-width:767px) { + .banner h2 { + margin: 0; + text-shadow: 2px 2px 3px rgba(0,0,0,0.6); + font-size: 3em; + } + + ul.banner-social-buttons > li { + display: block; + margin-bottom: 20px; + padding: 0; + } + + ul.banner-social-buttons > li:last-child { + margin-bottom: 0; + } +} + +footer { + padding: 50px 0; + background-color: #222; +} + +p.copyright { + margin: 15px 0 0; +} + + +.portfolio-modal .modal-content { + padding: 100px 0; + min-height: 100%; + border: 0; + border-radius: 0; + background-clip: border-box; + -webkit-box-shadow: none; + box-shadow: none; +} + +.portfolio-modal .modal-content h2 { + margin-bottom: 15px; + font-size: 3em; +} + +.portfolio-modal .modal-content p { + margin-bottom: 30px; +} + +.portfolio-modal .modal-content p.item-intro { + margin: 20px 0 30px; + font-family: "Droid Serif","Helvetica Neue",Helvetica,Arial,sans-serif; + font-size: 16px; + font-style: italic; +} + +.portfolio-modal .modal-content ul.list-inline { + margin-top: 0; + margin-bottom: 30px; +} + +.portfolio-modal .modal-content img { + margin-bottom: 30px; +} + +.portfolio-modal .close-modal { + position: absolute; + top: 25px; + right: 25px; + width: 75px; + height: 75px; + background-color: transparent; + cursor: pointer; +} + +.portfolio-modal .close-modal:hover { + opacity: .3; +} + +.portfolio-modal .close-modal .lr { + z-index: 1051; + width: 1px; + height: 75px; + margin-left: 35px; + background-color: #222; + -webkit-transform: rotate(45deg); + -ms-transform: rotate(45deg); + transform: rotate(45deg); +} + +.portfolio-modal .close-modal .lr .rl { + z-index: 1052; + width: 1px; + height: 75px; + background-color: #222; + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); +} + +.icon-mentoring { + color: #333; + vertical-align: middle; +} + +#portfolio .portfolio-item { + right: 0; + margin: 0 0 15px; +} + +#portfolio .portfolio-item .portfolio-link { + display: block; + position: relative; + margin: 0 auto; + max-width: 400px; +} + +#portfolio .portfolio-item .portfolio-link .portfolio-hover { + position: absolute; + width: 100%; + height: 100%; + opacity: 0; + -webkit-transition: all ease .5s; + -moz-transition: all ease .5s; + transition: all ease .5s; + background: rgba(254, 209, 54, .9); +} + +#portfolio .portfolio-item .portfolio-link .portfolio-hover:hover { + opacity: 1; +} + +#portfolio .portfolio-item .portfolio-link .portfolio-hover .portfolio-hover-content { + position: absolute; + top: 50%; + width: 100%; + height: 20px; + margin-top: -12px; + text-align: center; + font-size: 20px; + color: #fff; +} + +#portfolio .portfolio-item .portfolio-link .portfolio-hover .portfolio-hover-content i { + margin-top: -12px; +} + +#portfolio .portfolio-item .portfolio-link .portfolio-hover .portfolio-hover-content h3, +#portfolio .portfolio-item .portfolio-link .portfolio-hover .portfolio-hover-content h4 { + margin: 0; +} + +#portfolio .portfolio-item .portfolio-caption { + margin: 0 auto; + padding: 25px; + max-width: 400px; + text-align: center; + background-color: #fff; +} + +#portfolio .portfolio-item .portfolio-caption h4 { + margin: 0; + text-transform: none; +} + +#portfolio .portfolio-item .portfolio-caption p { + margin: 0; + font-family: "Droid Serif","Helvetica Neue",Helvetica,Arial,sans-serif; + font-size: 16px; + font-style: italic; +} + +#portfolio * { + z-index: 2; +} + +@media(min-width:767px) { + #portfolio .portfolio-item { + margin: 0 0 30px; + } +} diff --git a/gsoc.html b/gsoc.html new file mode 100644 index 0000000..ee9650e --- /dev/null +++ b/gsoc.html @@ -0,0 +1,40 @@ +--- +layout: default +title: openSUSE @ GSoC +slug: gsoc +--- +
+
+
+
+
+
+

{{page.title}}

+
+
+
+
+
+
+
+ +
+
+
+
+

Flip bits, not burgers!

+

+ Google Summer of Code is a global program that offers students a + stipend to write code for free software projects. openSUSE + has participated in Google Summer of Code since 2006 and has + helped more than 50 students to get started with free software! +

+

+ This year openSUSE is participating again with {{ site.gsoc_current.size }} projects. +

+
+
+
+
+ +{% include gsoc-accepted-projects.html %} diff --git a/img/banner-bg.jpg b/img/banner-bg.jpg new file mode 100644 index 0000000..9dcebbf Binary files /dev/null and b/img/banner-bg.jpg differ diff --git a/img/community.png b/img/community.png new file mode 100644 index 0000000..3eb561a Binary files /dev/null and b/img/community.png differ diff --git a/img/desk.jpg b/img/desk.jpg new file mode 100644 index 0000000..5322f78 Binary files /dev/null and b/img/desk.jpg differ diff --git a/img/favicon.png b/img/favicon.png new file mode 100644 index 0000000..df9a690 Binary files /dev/null and b/img/favicon.png differ diff --git a/img/geek.jpg b/img/geek.jpg new file mode 100644 index 0000000..4c97c68 Binary files /dev/null and b/img/geek.jpg differ diff --git a/img/intro-bg.jpg b/img/intro-bg.jpg new file mode 100644 index 0000000..f81a649 Binary files /dev/null and b/img/intro-bg.jpg differ diff --git a/img/mentor-intro-bg.jpg b/img/mentor-intro-bg.jpg new file mode 100644 index 0000000..67b2f62 Binary files /dev/null and b/img/mentor-intro-bg.jpg differ diff --git a/img/outside.jpg b/img/outside.jpg new file mode 100644 index 0000000..7a243fb Binary files /dev/null and b/img/outside.jpg differ diff --git a/img/outside2.jpg b/img/outside2.jpg new file mode 100644 index 0000000..0986291 Binary files /dev/null and b/img/outside2.jpg differ diff --git a/img/projects/cloudcv.png b/img/projects/cloudcv.png new file mode 100644 index 0000000..df9a690 Binary files /dev/null and b/img/projects/cloudcv.png differ diff --git a/img/projects/cvfy.png b/img/projects/cvfy.png new file mode 100755 index 0000000..1057341 Binary files /dev/null and b/img/projects/cvfy.png differ diff --git a/img/projects/evalai.png b/img/projects/evalai.png new file mode 100644 index 0000000..2f2d2bc Binary files /dev/null and b/img/projects/evalai.png differ diff --git a/img/projects/ide.png b/img/projects/ide.png new file mode 100644 index 0000000..3ae9c6b Binary files /dev/null and b/img/projects/ide.png differ diff --git a/img/projects/jangouts.png b/img/projects/jangouts.png new file mode 100644 index 0000000..399039b Binary files /dev/null and b/img/projects/jangouts.png differ diff --git a/img/projects/machinery.png b/img/projects/machinery.png new file mode 100644 index 0000000..37bf842 Binary files /dev/null and b/img/projects/machinery.png differ diff --git a/img/projects/obs.png b/img/projects/obs.png new file mode 100644 index 0000000..c3061c3 Binary files /dev/null and b/img/projects/obs.png differ diff --git a/img/projects/openSUSE.png b/img/projects/openSUSE.png new file mode 100644 index 0000000..91746c0 Binary files /dev/null and b/img/projects/openSUSE.png differ diff --git a/img/projects/osem.png b/img/projects/osem.png new file mode 100644 index 0000000..9fd4331 Binary files /dev/null and b/img/projects/osem.png differ diff --git a/img/projects/portus.png b/img/projects/portus.png new file mode 100644 index 0000000..262aed1 Binary files /dev/null and b/img/projects/portus.png differ diff --git a/img/projects/yast.png b/img/projects/yast.png new file mode 100644 index 0000000..edcfc26 Binary files /dev/null and b/img/projects/yast.png differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..d44891e --- /dev/null +++ b/index.html @@ -0,0 +1,118 @@ +--- +layout: default +title: CloudCV – GSoC Ideas +redirect_from: + - /mentor/index.html +--- + +{% include header.html %} + +
+
+
+
+

About CloudCV

+

+ {% include teaser.md %} +

+ +
+
+
+
+ +
+ + +{% for post in site.posts reversed %} + {% if post.type == 'project' %} + {% capture thecycle %}{% cycle 'odd', 'even' %}{% endcapture %} + {% if thecycle == 'odd' %} +
+
+
+
+
+
+

{{ post.title }}

+

+ {% for technology in post.technologies %} + {{ technology }} + {% endfor %} +

+
{{ post.content }} +
+

Please wait for loading the projects ...

+
+ + + + + + + + + + + +
+
+ + +
+
+
+ +
+
+
+ +
+ {% else %} +
+
+
+
+
+
+

{{ post.title }}

+

+ {% for technology in post.technologies %} + {{ technology }} + {% endfor %} +

+
{{ post.content }}
+

Please wait for loading the projects ...

+
+ + + + + + + + + + + +
+
+ + +
+
+
+ +
+
+ +
+
+ {% endif %} + {% endif %} +{% endfor %} + +{% include mentor.html %} +{% include mentor_process.html %} diff --git a/js/github.js b/js/github.js new file mode 100644 index 0000000..7e1048c --- /dev/null +++ b/js/github.js @@ -0,0 +1,84 @@ +$(document).ready(function () { + var url = $('table').first().data('url'); + + // GitHub allows only 60 API calls per hour from the same IP address, + // therefore we check first if we have API calls left. + // If not we display a proper error message! + var remaining; + $.getJSON('https://api.github.com/rate_limit', function (data) { + remaining = data.rate.remaining; + console.log(remaining) + if(remaining > 0) { + console.log(url) + $.getJSON(url, function (data) { + $.each(data, function (index) { + + // Assignee can be nil + var mentor = this.assignee ? this.assignee.login : this.user.login; + var mentor_url = this.assignee ? this.assignee.html_url : this.user.html_url; + + // Add the rows to the tables + var row = "" + this.title + "" + + " together with " + mentor + "" + + "Yes, let's do it"; + + console.log(row) + console.log(this.labels) + + $.each(this.labels, function () { + console.log(this.name) + $('.' + this.name + '-table tbody').append(row); + $('.' + this.name + '-table').show(); + $('.' + this.name + '-placeholder').remove(); + }); + + gsoc_hint = get_gsoc_hint(this.labels); + + // Add the modal for the project + var modal = ""; + $('footer').after(modal); + $('.project-placeholder').html("Sorry but currently we don't have any mentoring project ...") + }); + }); + } + else{ + $('.project-placeholder').html("Sorry but it seems like you exceeded the allowed number of requests. Please have a look at our issues!") + } + }); +}); + +function get_gsoc_hint(labels){ + result = ""; + current_url = window.location.href; + for(var i = 0; i < labels.length; i++) { + if (labels[i].name == 'GSoC') { + if(current_url.indexOf('gsoc') > -1) { + url = current_url; + } + else{ + url = current_url + "/gsoc"; + } + result = "

You can do this project as part of the Google Summer of Code program. Click here for more information.

" + break; + } + } + return result; +} diff --git a/js/landing-page.js b/js/landing-page.js new file mode 100644 index 0000000..47caa0f --- /dev/null +++ b/js/landing-page.js @@ -0,0 +1,33 @@ +// jQuery for page scrolling feature - requires jQuery Easing plugin +$(function() { + $('a.page-scroll').bind('click', function(event) { + var $anchor = $(this); + // this is monkey patched... + nav_link = $anchor.attr('href').split('#'); + $('html, body').stop().animate({ + scrollTop: $('#' + nav_link[1]).offset().top + }, 1500, 'easeInOutExpo'); + event.preventDefault(); + }); +}); + +// Highlight the top nav as scrolling occurs +$('body').scrollspy({ + target: '.navbar-fixed-top' +}) + +// Closes the Responsive Menu on Menu Item Click +$('.navbar-collapse ul li a').click(function() { + $('.navbar-toggle:visible').click(); +}); + +$('div.modal').on('show.bs.modal', function() { + var modal = this; + var hash = modal.id; + window.location.hash = hash; + window.onhashchange = function() { + if (!location.hash){ + $(modal).modal('hide'); + } + } +}); \ No newline at end of file diff --git a/js/markdown.js b/js/markdown.js new file mode 100644 index 0000000..d6faac6 --- /dev/null +++ b/js/markdown.js @@ -0,0 +1,1725 @@ +// Released under MIT license +// Copyright (c) 2009-2010 Dominic Baggott +// Copyright (c) 2009-2010 Ash Berlin +// Copyright (c) 2011 Christoph Dorn (http://www.christophdorn.com) + +/*jshint browser:true, devel:true */ + +(function( expose ) { + + /** + * class Markdown + * + * Markdown processing in Javascript done right. We have very particular views + * on what constitutes 'right' which include: + * + * - produces well-formed HTML (this means that em and strong nesting is + * important) + * + * - has an intermediate representation to allow processing of parsed data (We + * in fact have two, both as [JsonML]: a markdown tree and an HTML tree). + * + * - is easily extensible to add new dialects without having to rewrite the + * entire parsing mechanics + * + * - has a good test suite + * + * This implementation fulfills all of these (except that the test suite could + * do with expanding to automatically run all the fixtures from other Markdown + * implementations.) + * + * ##### Intermediate Representation + * + * *TODO* Talk about this :) Its JsonML, but document the node names we use. + * + * [JsonML]: http://jsonml.org/ "JSON Markup Language" + **/ + var Markdown = expose.Markdown = function(dialect) { + switch (typeof dialect) { + case "undefined": + this.dialect = Markdown.dialects.Gruber; + break; + case "object": + this.dialect = dialect; + break; + default: + if ( dialect in Markdown.dialects ) { + this.dialect = Markdown.dialects[dialect]; + } + else { + throw new Error("Unknown Markdown dialect '" + String(dialect) + "'"); + } + break; + } + this.em_state = []; + this.strong_state = []; + this.debug_indent = ""; + }; + + /** + * parse( markdown, [dialect] ) -> JsonML + * - markdown (String): markdown string to parse + * - dialect (String | Dialect): the dialect to use, defaults to gruber + * + * Parse `markdown` and return a markdown document as a Markdown.JsonML tree. + **/ + expose.parse = function( source, dialect ) { + // dialect will default if undefined + var md = new Markdown( dialect ); + return md.toTree( source ); + }; + + /** + * toHTML( markdown, [dialect] ) -> String + * toHTML( md_tree ) -> String + * - markdown (String): markdown string to parse + * - md_tree (Markdown.JsonML): parsed markdown tree + * + * Take markdown (either as a string or as a JsonML tree) and run it through + * [[toHTMLTree]] then turn it into a well-formated HTML fragment. + **/ + expose.toHTML = function toHTML( source , dialect , options ) { + var input = expose.toHTMLTree( source , dialect , options ); + + return expose.renderJsonML( input ); + }; + + /** + * toHTMLTree( markdown, [dialect] ) -> JsonML + * toHTMLTree( md_tree ) -> JsonML + * - markdown (String): markdown string to parse + * - dialect (String | Dialect): the dialect to use, defaults to gruber + * - md_tree (Markdown.JsonML): parsed markdown tree + * + * Turn markdown into HTML, represented as a JsonML tree. If a string is given + * to this function, it is first parsed into a markdown tree by calling + * [[parse]]. + **/ + expose.toHTMLTree = function toHTMLTree( input, dialect , options ) { + // convert string input to an MD tree + if ( typeof input ==="string" ) input = this.parse( input, dialect ); + + // Now convert the MD tree to an HTML tree + + // remove references from the tree + var attrs = extract_attr( input ), + refs = {}; + + if ( attrs && attrs.references ) { + refs = attrs.references; + } + + var html = convert_tree_to_html( input, refs , options ); + merge_text_nodes( html ); + return html; + }; + +// For Spidermonkey based engines + function mk_block_toSource() { + return "Markdown.mk_block( " + + uneval(this.toString()) + + ", " + + uneval(this.trailing) + + ", " + + uneval(this.lineNumber) + + " )"; + } + +// node + function mk_block_inspect() { + var util = require("util"); + return "Markdown.mk_block( " + + util.inspect(this.toString()) + + ", " + + util.inspect(this.trailing) + + ", " + + util.inspect(this.lineNumber) + + " )"; + + } + + var mk_block = Markdown.mk_block = function(block, trail, line) { + // Be helpful for default case in tests. + if ( arguments.length == 1 ) trail = "\n\n"; + + var s = new String(block); + s.trailing = trail; + // To make it clear its not just a string + s.inspect = mk_block_inspect; + s.toSource = mk_block_toSource; + + if ( line != undefined ) + s.lineNumber = line; + + return s; + }; + + function count_lines( str ) { + var n = 0, i = -1; + while ( ( i = str.indexOf("\n", i + 1) ) !== -1 ) n++; + return n; + } + +// Internal - split source into rough blocks + Markdown.prototype.split_blocks = function splitBlocks( input, startLine ) { + input = input.replace(/(\r\n|\n|\r)/g, "\n"); + // [\s\S] matches _anything_ (newline or space) + // [^] is equivalent but doesn't work in IEs. + var re = /([\s\S]+?)($|\n#|\n(?:\s*\n|$)+)/g, + blocks = [], + m; + + var line_no = 1; + + if ( ( m = /^(\s*\n)/.exec(input) ) != null ) { + // skip (but count) leading blank lines + line_no += count_lines( m[0] ); + re.lastIndex = m[0].length; + } + + while ( ( m = re.exec(input) ) !== null ) { + if (m[2] == "\n#") { + m[2] = "\n"; + re.lastIndex--; + } + blocks.push( mk_block( m[1], m[2], line_no ) ); + line_no += count_lines( m[0] ); + } + + return blocks; + }; + + /** + * Markdown#processBlock( block, next ) -> undefined | [ JsonML, ... ] + * - block (String): the block to process + * - next (Array): the following blocks + * + * Process `block` and return an array of JsonML nodes representing `block`. + * + * It does this by asking each block level function in the dialect to process + * the block until one can. Succesful handling is indicated by returning an + * array (with zero or more JsonML nodes), failure by a false value. + * + * Blocks handlers are responsible for calling [[Markdown#processInline]] + * themselves as appropriate. + * + * If the blocks were split incorrectly or adjacent blocks need collapsing you + * can adjust `next` in place using shift/splice etc. + * + * If any of this default behaviour is not right for the dialect, you can + * define a `__call__` method on the dialect that will get invoked to handle + * the block processing. + */ + Markdown.prototype.processBlock = function processBlock( block, next ) { + var cbs = this.dialect.block, + ord = cbs.__order__; + + if ( "__call__" in cbs ) { + return cbs.__call__.call(this, block, next); + } + + for ( var i = 0; i < ord.length; i++ ) { + //D:this.debug( "Testing", ord[i] ); + var res = cbs[ ord[i] ].call( this, block, next ); + if ( res ) { + //D:this.debug(" matched"); + if ( !isArray(res) || ( res.length > 0 && !( isArray(res[0]) ) ) ) + this.debug(ord[i], "didn't return a proper array"); + //D:this.debug( "" ); + return res; + } + } + + // Uhoh! no match! Should we throw an error? + return []; + }; + + Markdown.prototype.processInline = function processInline( block ) { + return this.dialect.inline.__call__.call( this, String( block ) ); + }; + + /** + * Markdown#toTree( source ) -> JsonML + * - source (String): markdown source to parse + * + * Parse `source` into a JsonML tree representing the markdown document. + **/ +// custom_tree means set this.tree to `custom_tree` and restore old value on return + Markdown.prototype.toTree = function toTree( source, custom_root ) { + var blocks = source instanceof Array ? source : this.split_blocks( source ); + + // Make tree a member variable so its easier to mess with in extensions + var old_tree = this.tree; + try { + this.tree = custom_root || this.tree || [ "markdown" ]; + + blocks: + while ( blocks.length ) { + var b = this.processBlock( blocks.shift(), blocks ); + + // Reference blocks and the like won't return any content + if ( !b.length ) continue blocks; + + this.tree.push.apply( this.tree, b ); + } + return this.tree; + } + finally { + if ( custom_root ) { + this.tree = old_tree; + } + } + }; + +// Noop by default + Markdown.prototype.debug = function () { + var args = Array.prototype.slice.call( arguments); + args.unshift(this.debug_indent); + if ( typeof print !== "undefined" ) + print.apply( print, args ); + if ( typeof console !== "undefined" && typeof console.log !== "undefined" ) + console.log.apply( null, args ); + } + + Markdown.prototype.loop_re_over_block = function( re, block, cb ) { + // Dont use /g regexps with this + var m, + b = block.valueOf(); + + while ( b.length && (m = re.exec(b) ) != null ) { + b = b.substr( m[0].length ); + cb.call(this, m); + } + return b; + }; + + /** + * Markdown.dialects + * + * Namespace of built-in dialects. + **/ + Markdown.dialects = {}; + + /** + * Markdown.dialects.Gruber + * + * The default dialect that follows the rules set out by John Gruber's + * markdown.pl as closely as possible. Well actually we follow the behaviour of + * that script which in some places is not exactly what the syntax web page + * says. + **/ + Markdown.dialects.Gruber = { + block: { + atxHeader: function atxHeader( block, next ) { + var m = block.match( /^(#{1,6})\s*(.*?)\s*#*\s*(?:\n|$)/ ); + + if ( !m ) return undefined; + + var header = [ "header", { level: m[ 1 ].length } ]; + Array.prototype.push.apply(header, this.processInline(m[ 2 ])); + + if ( m[0].length < block.length ) + next.unshift( mk_block( block.substr( m[0].length ), block.trailing, block.lineNumber + 2 ) ); + + return [ header ]; + }, + + setextHeader: function setextHeader( block, next ) { + var m = block.match( /^(.*)\n([-=])\2\2+(?:\n|$)/ ); + + if ( !m ) return undefined; + + var level = ( m[ 2 ] === "=" ) ? 1 : 2; + var header = [ "header", { level : level }, m[ 1 ] ]; + + if ( m[0].length < block.length ) + next.unshift( mk_block( block.substr( m[0].length ), block.trailing, block.lineNumber + 2 ) ); + + return [ header ]; + }, + + code: function code( block, next ) { + // | Foo + // |bar + // should be a code block followed by a paragraph. Fun + // + // There might also be adjacent code block to merge. + + var ret = [], + re = /^(?: {0,3}\t| {4})(.*)\n?/, + lines; + + // 4 spaces + content + if ( !block.match( re ) ) return undefined; + + block_search: + do { + // Now pull out the rest of the lines + var b = this.loop_re_over_block( + re, block.valueOf(), function( m ) { ret.push( m[1] ); } ); + + if ( b.length ) { + // Case alluded to in first comment. push it back on as a new block + next.unshift( mk_block(b, block.trailing) ); + break block_search; + } + else if ( next.length ) { + // Check the next block - it might be code too + if ( !next[0].match( re ) ) break block_search; + + // Pull how how many blanks lines follow - minus two to account for .join + ret.push ( block.trailing.replace(/[^\n]/g, "").substring(2) ); + + block = next.shift(); + } + else { + break block_search; + } + } while ( true ); + + return [ [ "code_block", ret.join("\n") ] ]; + }, + + horizRule: function horizRule( block, next ) { + // this needs to find any hr in the block to handle abutting blocks + var m = block.match( /^(?:([\s\S]*?)\n)?[ \t]*([-_*])(?:[ \t]*\2){2,}[ \t]*(?:\n([\s\S]*))?$/ ); + + if ( !m ) { + return undefined; + } + + var jsonml = [ [ "hr" ] ]; + + // if there's a leading abutting block, process it + if ( m[ 1 ] ) { + jsonml.unshift.apply( jsonml, this.processBlock( m[ 1 ], [] ) ); + } + + // if there's a trailing abutting block, stick it into next + if ( m[ 3 ] ) { + next.unshift( mk_block( m[ 3 ] ) ); + } + + return jsonml; + }, + + // There are two types of lists. Tight and loose. Tight lists have no whitespace + // between the items (and result in text just in the
  • ) and loose lists, + // which have an empty line between list items, resulting in (one or more) + // paragraphs inside the
  • . + // + // There are all sorts weird edge cases about the original markdown.pl's + // handling of lists: + // + // * Nested lists are supposed to be indented by four chars per level. But + // if they aren't, you can get a nested list by indenting by less than + // four so long as the indent doesn't match an indent of an existing list + // item in the 'nest stack'. + // + // * The type of the list (bullet or number) is controlled just by the + // first item at the indent. Subsequent changes are ignored unless they + // are for nested lists + // + lists: (function( ) { + // Use a closure to hide a few variables. + var any_list = "[*+-]|\\d+\\.", + bullet_list = /[*+-]/, + number_list = /\d+\./, + // Capture leading indent as it matters for determining nested lists. + is_list_re = new RegExp( "^( {0,3})(" + any_list + ")[ \t]+" ), + indent_re = "(?: {0,3}\\t| {4})"; + + // TODO: Cache this regexp for certain depths. + // Create a regexp suitable for matching an li for a given stack depth + function regex_for_depth( depth ) { + + return new RegExp( + // m[1] = indent, m[2] = list_type + "(?:^(" + indent_re + "{0," + depth + "} {0,3})(" + any_list + ")\\s+)|" + + // m[3] = cont + "(^" + indent_re + "{0," + (depth-1) + "}[ ]{0,4})" + ); + } + function expand_tab( input ) { + return input.replace( / {0,3}\t/g, " " ); + } + + // Add inline content `inline` to `li`. inline comes from processInline + // so is an array of content + function add(li, loose, inline, nl) { + if ( loose ) { + li.push( [ "para" ].concat(inline) ); + return; + } + // Hmmm, should this be any block level element or just paras? + var add_to = li[li.length -1] instanceof Array && li[li.length - 1][0] == "para" + ? li[li.length -1] + : li; + + // If there is already some content in this list, add the new line in + if ( nl && li.length > 1 ) inline.unshift(nl); + + for ( var i = 0; i < inline.length; i++ ) { + var what = inline[i], + is_str = typeof what == "string"; + if ( is_str && add_to.length > 1 && typeof add_to[add_to.length-1] == "string" ) { + add_to[ add_to.length-1 ] += what; + } + else { + add_to.push( what ); + } + } + } + + // contained means have an indent greater than the current one. On + // *every* line in the block + function get_contained_blocks( depth, blocks ) { + + var re = new RegExp( "^(" + indent_re + "{" + depth + "}.*?\\n?)*$" ), + replace = new RegExp("^" + indent_re + "{" + depth + "}", "gm"), + ret = []; + + while ( blocks.length > 0 ) { + if ( re.exec( blocks[0] ) ) { + var b = blocks.shift(), + // Now remove that indent + x = b.replace( replace, ""); + + ret.push( mk_block( x, b.trailing, b.lineNumber ) ); + } + else { + break; + } + } + return ret; + } + + // passed to stack.forEach to turn list items up the stack into paras + function paragraphify(s, i, stack) { + var list = s.list; + var last_li = list[list.length-1]; + + if ( last_li[1] instanceof Array && last_li[1][0] == "para" ) { + return; + } + if ( i + 1 == stack.length ) { + // Last stack frame + // Keep the same array, but replace the contents + last_li.push( ["para"].concat( last_li.splice(1, last_li.length - 1) ) ); + } + else { + var sublist = last_li.pop(); + last_li.push( ["para"].concat( last_li.splice(1, last_li.length - 1) ), sublist ); + } + } + + // The matcher function + return function( block, next ) { + var m = block.match( is_list_re ); + if ( !m ) return undefined; + + function make_list( m ) { + var list = bullet_list.exec( m[2] ) + ? ["bulletlist"] + : ["numberlist"]; + + stack.push( { list: list, indent: m[1] } ); + return list; + } + + + var stack = [], // Stack of lists for nesting. + list = make_list( m ), + last_li, + loose = false, + ret = [ stack[0].list ], + i; + + // Loop to search over block looking for inner block elements and loose lists + loose_search: + while ( true ) { + // Split into lines preserving new lines at end of line + var lines = block.split( /(?=\n)/ ); + + // We have to grab all lines for a li and call processInline on them + // once as there are some inline things that can span lines. + var li_accumulate = ""; + + // Loop over the lines in this block looking for tight lists. + tight_search: + for ( var line_no = 0; line_no < lines.length; line_no++ ) { + var nl = "", + l = lines[line_no].replace(/^\n/, function(n) { nl = n; return ""; }); + + // TODO: really should cache this + var line_re = regex_for_depth( stack.length ); + + m = l.match( line_re ); + //print( "line:", uneval(l), "\nline match:", uneval(m) ); + + // We have a list item + if ( m[1] !== undefined ) { + // Process the previous list item, if any + if ( li_accumulate.length ) { + add( last_li, loose, this.processInline( li_accumulate ), nl ); + // Loose mode will have been dealt with. Reset it + loose = false; + li_accumulate = ""; + } + + m[1] = expand_tab( m[1] ); + var wanted_depth = Math.floor(m[1].length/4)+1; + //print( "want:", wanted_depth, "stack:", stack.length); + if ( wanted_depth > stack.length ) { + // Deep enough for a nested list outright + //print ( "new nested list" ); + list = make_list( m ); + last_li.push( list ); + last_li = list[1] = [ "listitem" ]; + } + else { + // We aren't deep enough to be strictly a new level. This is + // where Md.pl goes nuts. If the indent matches a level in the + // stack, put it there, else put it one deeper then the + // wanted_depth deserves. + var found = false; + for ( i = 0; i < stack.length; i++ ) { + if ( stack[ i ].indent != m[1] ) continue; + list = stack[ i ].list; + stack.splice( i+1, stack.length - (i+1) ); + found = true; + break; + } + + if (!found) { + //print("not found. l:", uneval(l)); + wanted_depth++; + if ( wanted_depth <= stack.length ) { + stack.splice(wanted_depth, stack.length - wanted_depth); + //print("Desired depth now", wanted_depth, "stack:", stack.length); + list = stack[wanted_depth-1].list; + //print("list:", uneval(list) ); + } + else { + //print ("made new stack for messy indent"); + list = make_list(m); + last_li.push(list); + } + } + + //print( uneval(list), "last", list === stack[stack.length-1].list ); + last_li = [ "listitem" ]; + list.push(last_li); + } // end depth of shenegains + nl = ""; + } + + // Add content + if ( l.length > m[0].length ) { + li_accumulate += nl + l.substr( m[0].length ); + } + } // tight_search + + if ( li_accumulate.length ) { + add( last_li, loose, this.processInline( li_accumulate ), nl ); + // Loose mode will have been dealt with. Reset it + loose = false; + li_accumulate = ""; + } + + // Look at the next block - we might have a loose list. Or an extra + // paragraph for the current li + var contained = get_contained_blocks( stack.length, next ); + + // Deal with code blocks or properly nested lists + if ( contained.length > 0 ) { + // Make sure all listitems up the stack are paragraphs + forEach( stack, paragraphify, this); + + last_li.push.apply( last_li, this.toTree( contained, [] ) ); + } + + var next_block = next[0] && next[0].valueOf() || ""; + + if ( next_block.match(is_list_re) || next_block.match( /^ / ) ) { + block = next.shift(); + + // Check for an HR following a list: features/lists/hr_abutting + var hr = this.dialect.block.horizRule( block, next ); + + if ( hr ) { + ret.push.apply(ret, hr); + break; + } + + // Make sure all listitems up the stack are paragraphs + forEach( stack, paragraphify, this); + + loose = true; + continue loose_search; + } + break; + } // loose_search + + return ret; + }; + })(), + + blockquote: function blockquote( block, next ) { + if ( !block.match( /^>/m ) ) + return undefined; + + var jsonml = []; + + // separate out the leading abutting block, if any. I.e. in this case: + // + // a + // > b + // + if ( block[ 0 ] != ">" ) { + var lines = block.split( /\n/ ), + prev = [], + line_no = block.lineNumber; + + // keep shifting lines until you find a crotchet + while ( lines.length && lines[ 0 ][ 0 ] != ">" ) { + prev.push( lines.shift() ); + line_no++; + } + + var abutting = mk_block( prev.join( "\n" ), "\n", block.lineNumber ); + jsonml.push.apply( jsonml, this.processBlock( abutting, [] ) ); + // reassemble new block of just block quotes! + block = mk_block( lines.join( "\n" ), block.trailing, line_no ); + } + + + // if the next block is also a blockquote merge it in + while ( next.length && next[ 0 ][ 0 ] == ">" ) { + var b = next.shift(); + block = mk_block( block + block.trailing + b, b.trailing, block.lineNumber ); + } + + // Strip off the leading "> " and re-process as a block. + var input = block.replace( /^> ?/gm, "" ), + old_tree = this.tree, + processedBlock = this.toTree( input, [ "blockquote" ] ), + attr = extract_attr( processedBlock ); + + // If any link references were found get rid of them + if ( attr && attr.references ) { + delete attr.references; + // And then remove the attribute object if it's empty + if ( isEmpty( attr ) ) { + processedBlock.splice( 1, 1 ); + } + } + + jsonml.push( processedBlock ); + return jsonml; + }, + + referenceDefn: function referenceDefn( block, next) { + var re = /^\s*\[(.*?)\]:\s*(\S+)(?:\s+(?:(['"])(.*?)\3|\((.*?)\)))?\n?/; + // interesting matches are [ , ref_id, url, , title, title ] + + if ( !block.match(re) ) + return undefined; + + // make an attribute node if it doesn't exist + if ( !extract_attr( this.tree ) ) { + this.tree.splice( 1, 0, {} ); + } + + var attrs = extract_attr( this.tree ); + + // make a references hash if it doesn't exist + if ( attrs.references === undefined ) { + attrs.references = {}; + } + + var b = this.loop_re_over_block(re, block, function( m ) { + + if ( m[2] && m[2][0] == "<" && m[2][m[2].length-1] == ">" ) + m[2] = m[2].substring( 1, m[2].length - 1 ); + + var ref = attrs.references[ m[1].toLowerCase() ] = { + href: m[2] + }; + + if ( m[4] !== undefined ) + ref.title = m[4]; + else if ( m[5] !== undefined ) + ref.title = m[5]; + + } ); + + if ( b.length ) + next.unshift( mk_block( b, block.trailing ) ); + + return []; + }, + + para: function para( block, next ) { + // everything's a para! + return [ ["para"].concat( this.processInline( block ) ) ]; + } + } + }; + + Markdown.dialects.Gruber.inline = { + + __oneElement__: function oneElement( text, patterns_or_re, previous_nodes ) { + var m, + res, + lastIndex = 0; + + patterns_or_re = patterns_or_re || this.dialect.inline.__patterns__; + var re = new RegExp( "([\\s\\S]*?)(" + (patterns_or_re.source || patterns_or_re) + ")" ); + + m = re.exec( text ); + if (!m) { + // Just boring text + return [ text.length, text ]; + } + else if ( m[1] ) { + // Some un-interesting text matched. Return that first + return [ m[1].length, m[1] ]; + } + + var res; + if ( m[2] in this.dialect.inline ) { + res = this.dialect.inline[ m[2] ].call( + this, + text.substr( m.index ), m, previous_nodes || [] ); + } + // Default for now to make dev easier. just slurp special and output it. + res = res || [ m[2].length, m[2] ]; + return res; + }, + + __call__: function inline( text, patterns ) { + + var out = [], + res; + + function add(x) { + //D:self.debug(" adding output", uneval(x)); + if ( typeof x == "string" && typeof out[out.length-1] == "string" ) + out[ out.length-1 ] += x; + else + out.push(x); + } + + while ( text.length > 0 ) { + res = this.dialect.inline.__oneElement__.call(this, text, patterns, out ); + text = text.substr( res.shift() ); + forEach(res, add ) + } + + return out; + }, + + // These characters are intersting elsewhere, so have rules for them so that + // chunks of plain text blocks don't include them + "]": function () {}, + "}": function () {}, + + __escape__ : /^\\[\\`\*_{}\[\]()#\+.!\-]/, + + "\\": function escaped( text ) { + // [ length of input processed, node/children to add... ] + // Only esacape: \ ` * _ { } [ ] ( ) # * + - . ! + if ( this.dialect.inline.__escape__.exec( text ) ) + return [ 2, text.charAt( 1 ) ]; + else + // Not an esacpe + return [ 1, "\\" ]; + }, + + "![": function image( text ) { + + // Unlike images, alt text is plain text only. no other elements are + // allowed in there + + // ![Alt text](/path/to/img.jpg "Optional title") + // 1 2 3 4 <--- captures + var m = text.match( /^!\[(.*?)\][ \t]*\([ \t]*([^")]*?)(?:[ \t]+(["'])(.*?)\3)?[ \t]*\)/ ); + + if ( m ) { + if ( m[2] && m[2][0] == "<" && m[2][m[2].length-1] == ">" ) + m[2] = m[2].substring( 1, m[2].length - 1 ); + + m[2] = this.dialect.inline.__call__.call( this, m[2], /\\/ )[0]; + + var attrs = { alt: m[1], href: m[2] || "" }; + if ( m[4] !== undefined) + attrs.title = m[4]; + + return [ m[0].length, [ "img", attrs ] ]; + } + + // ![Alt text][id] + m = text.match( /^!\[(.*?)\][ \t]*\[(.*?)\]/ ); + + if ( m ) { + // We can't check if the reference is known here as it likely wont be + // found till after. Check it in md tree->hmtl tree conversion + return [ m[0].length, [ "img_ref", { alt: m[1], ref: m[2].toLowerCase(), original: m[0] } ] ]; + } + + // Just consume the '![' + return [ 2, "![" ]; + }, + + "[": function link( text ) { + + var orig = String(text); + // Inline content is possible inside `link text` + var res = Markdown.DialectHelpers.inline_until_char.call( this, text.substr(1), "]" ); + + // No closing ']' found. Just consume the [ + if ( !res ) return [ 1, "[" ]; + + var consumed = 1 + res[ 0 ], + children = res[ 1 ], + link, + attrs; + + // At this point the first [...] has been parsed. See what follows to find + // out which kind of link we are (reference or direct url) + text = text.substr( consumed ); + + // [link text](/path/to/img.jpg "Optional title") + // 1 2 3 <--- captures + // This will capture up to the last paren in the block. We then pull + // back based on if there a matching ones in the url + // ([here](/url/(test)) + // The parens have to be balanced + var m = text.match( /^\s*\([ \t]*([^"']*)(?:[ \t]+(["'])(.*?)\2)?[ \t]*\)/ ); + if ( m ) { + var url = m[1]; + consumed += m[0].length; + + if ( url && url[0] == "<" && url[url.length-1] == ">" ) + url = url.substring( 1, url.length - 1 ); + + // If there is a title we don't have to worry about parens in the url + if ( !m[3] ) { + var open_parens = 1; // One open that isn't in the capture + for ( var len = 0; len < url.length; len++ ) { + switch ( url[len] ) { + case "(": + open_parens++; + break; + case ")": + if ( --open_parens == 0) { + consumed -= url.length - len; + url = url.substring(0, len); + } + break; + } + } + } + + // Process escapes only + url = this.dialect.inline.__call__.call( this, url, /\\/ )[0]; + + attrs = { href: url || "" }; + if ( m[3] !== undefined) + attrs.title = m[3]; + + link = [ "link", attrs ].concat( children ); + return [ consumed, link ]; + } + + // [Alt text][id] + // [Alt text] [id] + m = text.match( /^\s*\[(.*?)\]/ ); + + if ( m ) { + + consumed += m[ 0 ].length; + + // [links][] uses links as its reference + attrs = { ref: ( m[ 1 ] || String(children) ).toLowerCase(), original: orig.substr( 0, consumed ) }; + + link = [ "link_ref", attrs ].concat( children ); + + // We can't check if the reference is known here as it likely wont be + // found till after. Check it in md tree->hmtl tree conversion. + // Store the original so that conversion can revert if the ref isn't found. + return [ consumed, link ]; + } + + // [id] + // Only if id is plain (no formatting.) + if ( children.length == 1 && typeof children[0] == "string" ) { + + attrs = { ref: children[0].toLowerCase(), original: orig.substr( 0, consumed ) }; + link = [ "link_ref", attrs, children[0] ]; + return [ consumed, link ]; + } + + // Just consume the "[" + return [ 1, "[" ]; + }, + + + "<": function autoLink( text ) { + var m; + + if ( ( m = text.match( /^<(?:((https?|ftp|mailto):[^>]+)|(.*?@.*?\.[a-zA-Z]+))>/ ) ) != null ) { + if ( m[3] ) { + return [ m[0].length, [ "link", { href: "mailto:" + m[3] }, m[3] ] ]; + + } + else if ( m[2] == "mailto" ) { + return [ m[0].length, [ "link", { href: m[1] }, m[1].substr("mailto:".length ) ] ]; + } + else + return [ m[0].length, [ "link", { href: m[1] }, m[1] ] ]; + } + + return [ 1, "<" ]; + }, + + "`": function inlineCode( text ) { + // Inline code block. as many backticks as you like to start it + // Always skip over the opening ticks. + var m = text.match( /(`+)(([\s\S]*?)\1)/ ); + + if ( m && m[2] ) + return [ m[1].length + m[2].length, [ "inlinecode", m[3] ] ]; + else { + // TODO: No matching end code found - warn! + return [ 1, "`" ]; + } + }, + + " \n": function lineBreak( text ) { + return [ 3, [ "linebreak" ] ]; + } + + }; + +// Meta Helper/generator method for em and strong handling + function strong_em( tag, md ) { + + var state_slot = tag + "_state", + other_slot = tag == "strong" ? "em_state" : "strong_state"; + + function CloseTag(len) { + this.len_after = len; + this.name = "close_" + md; + } + + return function ( text, orig_match ) { + + if ( this[state_slot][0] == md ) { + // Most recent em is of this type + //D:this.debug("closing", md); + this[state_slot].shift(); + + // "Consume" everything to go back to the recrusion in the else-block below + return[ text.length, new CloseTag(text.length-md.length) ]; + } + else { + // Store a clone of the em/strong states + var other = this[other_slot].slice(), + state = this[state_slot].slice(); + + this[state_slot].unshift(md); + + //D:this.debug_indent += " "; + + // Recurse + var res = this.processInline( text.substr( md.length ) ); + //D:this.debug_indent = this.debug_indent.substr(2); + + var last = res[res.length - 1]; + + //D:this.debug("processInline from", tag + ": ", uneval( res ) ); + + var check = this[state_slot].shift(); + if ( last instanceof CloseTag ) { + res.pop(); + // We matched! Huzzah. + var consumed = text.length - last.len_after; + return [ consumed, [ tag ].concat(res) ]; + } + else { + // Restore the state of the other kind. We might have mistakenly closed it. + this[other_slot] = other; + this[state_slot] = state; + + // We can't reuse the processed result as it could have wrong parsing contexts in it. + return [ md.length, md ]; + } + } + }; // End returned function + } + + Markdown.dialects.Gruber.inline["**"] = strong_em("strong", "**"); + Markdown.dialects.Gruber.inline["__"] = strong_em("strong", "__"); + Markdown.dialects.Gruber.inline["*"] = strong_em("em", "*"); + Markdown.dialects.Gruber.inline["_"] = strong_em("em", "_"); + + +// Build default order from insertion order. + Markdown.buildBlockOrder = function(d) { + var ord = []; + for ( var i in d ) { + if ( i == "__order__" || i == "__call__" ) continue; + ord.push( i ); + } + d.__order__ = ord; + }; + +// Build patterns for inline matcher + Markdown.buildInlinePatterns = function(d) { + var patterns = []; + + for ( var i in d ) { + // __foo__ is reserved and not a pattern + if ( i.match( /^__.*__$/) ) continue; + var l = i.replace( /([\\.*+?|()\[\]{}])/g, "\\$1" ) + .replace( /\n/, "\\n" ); + patterns.push( i.length == 1 ? l : "(?:" + l + ")" ); + } + + patterns = patterns.join("|"); + d.__patterns__ = patterns; + //print("patterns:", uneval( patterns ) ); + + var fn = d.__call__; + d.__call__ = function(text, pattern) { + if ( pattern != undefined ) { + return fn.call(this, text, pattern); + } + else + { + return fn.call(this, text, patterns); + } + }; + }; + + Markdown.DialectHelpers = {}; + Markdown.DialectHelpers.inline_until_char = function( text, want ) { + var consumed = 0, + nodes = []; + + while ( true ) { + if ( text.charAt( consumed ) == want ) { + // Found the character we were looking for + consumed++; + return [ consumed, nodes ]; + } + + if ( consumed >= text.length ) { + // No closing char found. Abort. + return null; + } + + var res = this.dialect.inline.__oneElement__.call(this, text.substr( consumed ) ); + consumed += res[ 0 ]; + // Add any returned nodes. + nodes.push.apply( nodes, res.slice( 1 ) ); + } + } + +// Helper function to make sub-classing a dialect easier + Markdown.subclassDialect = function( d ) { + function Block() {} + Block.prototype = d.block; + function Inline() {} + Inline.prototype = d.inline; + + return { block: new Block(), inline: new Inline() }; + }; + + Markdown.buildBlockOrder ( Markdown.dialects.Gruber.block ); + Markdown.buildInlinePatterns( Markdown.dialects.Gruber.inline ); + + Markdown.dialects.Maruku = Markdown.subclassDialect( Markdown.dialects.Gruber ); + + Markdown.dialects.Maruku.processMetaHash = function processMetaHash( meta_string ) { + var meta = split_meta_hash( meta_string ), + attr = {}; + + for ( var i = 0; i < meta.length; ++i ) { + // id: #foo + if ( /^#/.test( meta[ i ] ) ) { + attr.id = meta[ i ].substring( 1 ); + } + // class: .foo + else if ( /^\./.test( meta[ i ] ) ) { + // if class already exists, append the new one + if ( attr["class"] ) { + attr["class"] = attr["class"] + meta[ i ].replace( /./, " " ); + } + else { + attr["class"] = meta[ i ].substring( 1 ); + } + } + // attribute: foo=bar + else if ( /\=/.test( meta[ i ] ) ) { + var s = meta[ i ].split( /\=/ ); + attr[ s[ 0 ] ] = s[ 1 ]; + } + } + + return attr; + } + + function split_meta_hash( meta_string ) { + var meta = meta_string.split( "" ), + parts = [ "" ], + in_quotes = false; + + while ( meta.length ) { + var letter = meta.shift(); + switch ( letter ) { + case " " : + // if we're in a quoted section, keep it + if ( in_quotes ) { + parts[ parts.length - 1 ] += letter; + } + // otherwise make a new part + else { + parts.push( "" ); + } + break; + case "'" : + case '"' : + // reverse the quotes and move straight on + in_quotes = !in_quotes; + break; + case "\\" : + // shift off the next letter to be used straight away. + // it was escaped so we'll keep it whatever it is + letter = meta.shift(); + default : + parts[ parts.length - 1 ] += letter; + break; + } + } + + return parts; + } + + Markdown.dialects.Maruku.block.document_meta = function document_meta( block, next ) { + // we're only interested in the first block + if ( block.lineNumber > 1 ) return undefined; + + // document_meta blocks consist of one or more lines of `Key: Value\n` + if ( ! block.match( /^(?:\w+:.*\n)*\w+:.*$/ ) ) return undefined; + + // make an attribute node if it doesn't exist + if ( !extract_attr( this.tree ) ) { + this.tree.splice( 1, 0, {} ); + } + + var pairs = block.split( /\n/ ); + for ( p in pairs ) { + var m = pairs[ p ].match( /(\w+):\s*(.*)$/ ), + key = m[ 1 ].toLowerCase(), + value = m[ 2 ]; + + this.tree[ 1 ][ key ] = value; + } + + // document_meta produces no content! + return []; + }; + + Markdown.dialects.Maruku.block.block_meta = function block_meta( block, next ) { + // check if the last line of the block is an meta hash + var m = block.match( /(^|\n) {0,3}\{:\s*((?:\\\}|[^\}])*)\s*\}$/ ); + if ( !m ) return undefined; + + // process the meta hash + var attr = this.dialect.processMetaHash( m[ 2 ] ); + + var hash; + + // if we matched ^ then we need to apply meta to the previous block + if ( m[ 1 ] === "" ) { + var node = this.tree[ this.tree.length - 1 ]; + hash = extract_attr( node ); + + // if the node is a string (rather than JsonML), bail + if ( typeof node === "string" ) return undefined; + + // create the attribute hash if it doesn't exist + if ( !hash ) { + hash = {}; + node.splice( 1, 0, hash ); + } + + // add the attributes in + for ( a in attr ) { + hash[ a ] = attr[ a ]; + } + + // return nothing so the meta hash is removed + return []; + } + + // pull the meta hash off the block and process what's left + var b = block.replace( /\n.*$/, "" ), + result = this.processBlock( b, [] ); + + // get or make the attributes hash + hash = extract_attr( result[ 0 ] ); + if ( !hash ) { + hash = {}; + result[ 0 ].splice( 1, 0, hash ); + } + + // attach the attributes to the block + for ( a in attr ) { + hash[ a ] = attr[ a ]; + } + + return result; + }; + + Markdown.dialects.Maruku.block.definition_list = function definition_list( block, next ) { + // one or more terms followed by one or more definitions, in a single block + var tight = /^((?:[^\s:].*\n)+):\s+([\s\S]+)$/, + list = [ "dl" ], + i, m; + + // see if we're dealing with a tight or loose block + if ( ( m = block.match( tight ) ) ) { + // pull subsequent tight DL blocks out of `next` + var blocks = [ block ]; + while ( next.length && tight.exec( next[ 0 ] ) ) { + blocks.push( next.shift() ); + } + + for ( var b = 0; b < blocks.length; ++b ) { + var m = blocks[ b ].match( tight ), + terms = m[ 1 ].replace( /\n$/, "" ).split( /\n/ ), + defns = m[ 2 ].split( /\n:\s+/ ); + + // print( uneval( m ) ); + + for ( i = 0; i < terms.length; ++i ) { + list.push( [ "dt", terms[ i ] ] ); + } + + for ( i = 0; i < defns.length; ++i ) { + // run inline processing over the definition + list.push( [ "dd" ].concat( this.processInline( defns[ i ].replace( /(\n)\s+/, "$1" ) ) ) ); + } + } + } + else { + return undefined; + } + + return [ list ]; + }; + +// splits on unescaped instances of @ch. If @ch is not a character the result +// can be unpredictable + + Markdown.dialects.Maruku.block.table = function table (block, next) { + + var _split_on_unescaped = function(s, ch) { + ch = ch || '\\s'; + if (ch.match(/^[\\|\[\]{}?*.+^$]$/)) { ch = '\\' + ch; } + var res = [ ], + r = new RegExp('^((?:\\\\.|[^\\\\' + ch + '])*)' + ch + '(.*)'), + m; + while(m = s.match(r)) { + res.push(m[1]); + s = m[2]; + } + res.push(s); + return res; + } + + var leading_pipe = /^ {0,3}\|(.+)\n {0,3}\|\s*([\-:]+[\-| :]*)\n((?:\s*\|.*(?:\n|$))*)(?=\n|$)/, + // find at least an unescaped pipe in each line + no_leading_pipe = /^ {0,3}(\S(?:\\.|[^\\|])*\|.*)\n {0,3}([\-:]+\s*\|[\-| :]*)\n((?:(?:\\.|[^\\|])*\|.*(?:\n|$))*)(?=\n|$)/, + i, m; + if (m = block.match(leading_pipe)) { + // remove leading pipes in contents + // (header and horizontal rule already have the leading pipe left out) + m[3] = m[3].replace(/^\s*\|/gm, ''); + } else if (! ( m = block.match(no_leading_pipe))) { + return undefined; + } + + var table = [ "table", [ "thead", [ "tr" ] ], [ "tbody" ] ]; + + // remove trailing pipes, then split on pipes + // (no escaped pipes are allowed in horizontal rule) + m[2] = m[2].replace(/\|\s*$/, '').split('|'); + + // process alignment + var html_attrs = [ ]; + forEach (m[2], function (s) { + if (s.match(/^\s*-+:\s*$/)) html_attrs.push({align: "right"}); + else if (s.match(/^\s*:-+\s*$/)) html_attrs.push({align: "left"}); + else if (s.match(/^\s*:-+:\s*$/)) html_attrs.push({align: "center"}); + else html_attrs.push({}); + }); + + // now for the header, avoid escaped pipes + m[1] = _split_on_unescaped(m[1].replace(/\|\s*$/, ''), '|'); + for (i = 0; i < m[1].length; i++) { + table[1][1].push(['th', html_attrs[i] || {}].concat( + this.processInline(m[1][i].trim()))); + } + + // now for body contents + forEach (m[3].replace(/\|\s*$/mg, '').split('\n'), function (row) { + var html_row = ['tr']; + row = _split_on_unescaped(row, '|'); + for (i = 0; i < row.length; i++) { + html_row.push(['td', html_attrs[i] || {}].concat(this.processInline(row[i].trim()))); + } + table[2].push(html_row); + }, this); + + return [table]; + } + + Markdown.dialects.Maruku.inline[ "{:" ] = function inline_meta( text, matches, out ) { + if ( !out.length ) { + return [ 2, "{:" ]; + } + + // get the preceeding element + var before = out[ out.length - 1 ]; + + if ( typeof before === "string" ) { + return [ 2, "{:" ]; + } + + // match a meta hash + var m = text.match( /^\{:\s*((?:\\\}|[^\}])*)\s*\}/ ); + + // no match, false alarm + if ( !m ) { + return [ 2, "{:" ]; + } + + // attach the attributes to the preceeding element + var meta = this.dialect.processMetaHash( m[ 1 ] ), + attr = extract_attr( before ); + + if ( !attr ) { + attr = {}; + before.splice( 1, 0, attr ); + } + + for ( var k in meta ) { + attr[ k ] = meta[ k ]; + } + + // cut out the string and replace it with nothing + return [ m[ 0 ].length, "" ]; + }; + + Markdown.dialects.Maruku.inline.__escape__ = /^\\[\\`\*_{}\[\]()#\+.!\-|:]/; + + Markdown.buildBlockOrder ( Markdown.dialects.Maruku.block ); + Markdown.buildInlinePatterns( Markdown.dialects.Maruku.inline ); + + var isArray = Array.isArray || function(obj) { + return Object.prototype.toString.call(obj) == "[object Array]"; + }; + + var forEach; +// Don't mess with Array.prototype. Its not friendly + if ( Array.prototype.forEach ) { + forEach = function( arr, cb, thisp ) { + return arr.forEach( cb, thisp ); + }; + } + else { + forEach = function(arr, cb, thisp) { + for (var i = 0; i < arr.length; i++) { + cb.call(thisp || arr, arr[i], i, arr); + } + } + } + + var isEmpty = function( obj ) { + for ( var key in obj ) { + if ( hasOwnProperty.call( obj, key ) ) { + return false; + } + } + + return true; + } + + function extract_attr( jsonml ) { + return isArray(jsonml) + && jsonml.length > 1 + && typeof jsonml[ 1 ] === "object" + && !( isArray(jsonml[ 1 ]) ) + ? jsonml[ 1 ] + : undefined; + } + + + + /** + * renderJsonML( jsonml[, options] ) -> String + * - jsonml (Array): JsonML array to render to XML + * - options (Object): options + * + * Converts the given JsonML into well-formed XML. + * + * The options currently understood are: + * + * - root (Boolean): wether or not the root node should be included in the + * output, or just its children. The default `false` is to not include the + * root itself. + */ + expose.renderJsonML = function( jsonml, options ) { + options = options || {}; + // include the root element in the rendered output? + options.root = options.root || false; + + var content = []; + + if ( options.root ) { + content.push( render_tree( jsonml ) ); + } + else { + jsonml.shift(); // get rid of the tag + if ( jsonml.length && typeof jsonml[ 0 ] === "object" && !( jsonml[ 0 ] instanceof Array ) ) { + jsonml.shift(); // get rid of the attributes + } + + while ( jsonml.length ) { + content.push( render_tree( jsonml.shift() ) ); + } + } + + return content.join( "\n\n" ); + }; + + function escapeHTML( text ) { + return text.replace( /&/g, "&" ) + .replace( //g, ">" ) + .replace( /"/g, """ ) + .replace( /'/g, "'" ); + } + + function render_tree( jsonml ) { + // basic case + if ( typeof jsonml === "string" ) { + return escapeHTML( jsonml ); + } + + var tag = jsonml.shift(), + attributes = {}, + content = []; + + if ( jsonml.length && typeof jsonml[ 0 ] === "object" && !( jsonml[ 0 ] instanceof Array ) ) { + attributes = jsonml.shift(); + } + + while ( jsonml.length ) { + content.push( render_tree( jsonml.shift() ) ); + } + + var tag_attrs = ""; + for ( var a in attributes ) { + tag_attrs += " " + a + '="' + escapeHTML( attributes[ a ] ) + '"'; + } + + // be careful about adding whitespace here for inline elements + if ( tag == "img" || tag == "br" || tag == "hr" ) { + return "<"+ tag + tag_attrs + "/>"; + } + else { + return "<"+ tag + tag_attrs + ">" + content.join( "" ) + ""; + } + } + + function convert_tree_to_html( tree, references, options ) { + var i; + options = options || {}; + + // shallow clone + var jsonml = tree.slice( 0 ); + + if ( typeof options.preprocessTreeNode === "function" ) { + jsonml = options.preprocessTreeNode(jsonml, references); + } + + // Clone attributes if they exist + var attrs = extract_attr( jsonml ); + if ( attrs ) { + jsonml[ 1 ] = {}; + for ( i in attrs ) { + jsonml[ 1 ][ i ] = attrs[ i ]; + } + attrs = jsonml[ 1 ]; + } + + // basic case + if ( typeof jsonml === "string" ) { + return jsonml; + } + + // convert this node + switch ( jsonml[ 0 ] ) { + case "header": + jsonml[ 0 ] = "h" + jsonml[ 1 ].level; + delete jsonml[ 1 ].level; + break; + case "bulletlist": + jsonml[ 0 ] = "ul"; + break; + case "numberlist": + jsonml[ 0 ] = "ol"; + break; + case "listitem": + jsonml[ 0 ] = "li"; + break; + case "para": + jsonml[ 0 ] = "p"; + break; + case "markdown": + jsonml[ 0 ] = "html"; + if ( attrs ) delete attrs.references; + break; + case "code_block": + jsonml[ 0 ] = "pre"; + i = attrs ? 2 : 1; + var code = [ "code" ]; + code.push.apply( code, jsonml.splice( i, jsonml.length - i ) ); + jsonml[ i ] = code; + break; + case "inlinecode": + jsonml[ 0 ] = "code"; + break; + case "img": + jsonml[ 1 ].src = jsonml[ 1 ].href; + delete jsonml[ 1 ].href; + break; + case "linebreak": + jsonml[ 0 ] = "br"; + break; + case "link": + jsonml[ 0 ] = "a"; + break; + case "link_ref": + jsonml[ 0 ] = "a"; + + // grab this ref and clean up the attribute node + var ref = references[ attrs.ref ]; + + // if the reference exists, make the link + if ( ref ) { + delete attrs.ref; + + // add in the href and title, if present + attrs.href = ref.href; + if ( ref.title ) { + attrs.title = ref.title; + } + + // get rid of the unneeded original text + delete attrs.original; + } + // the reference doesn't exist, so revert to plain text + else { + return attrs.original; + } + break; + case "img_ref": + jsonml[ 0 ] = "img"; + + // grab this ref and clean up the attribute node + var ref = references[ attrs.ref ]; + + // if the reference exists, make the link + if ( ref ) { + delete attrs.ref; + + // add in the href and title, if present + attrs.src = ref.href; + if ( ref.title ) { + attrs.title = ref.title; + } + + // get rid of the unneeded original text + delete attrs.original; + } + // the reference doesn't exist, so revert to plain text + else { + return attrs.original; + } + break; + } + + // convert all the children + i = 1; + + // deal with the attribute node, if it exists + if ( attrs ) { + // if there are keys, skip over it + for ( var key in jsonml[ 1 ] ) { + i = 2; + break; + } + // if there aren't, remove it + if ( i === 1 ) { + jsonml.splice( i, 1 ); + } + } + + for ( ; i < jsonml.length; ++i ) { + jsonml[ i ] = convert_tree_to_html( jsonml[ i ], references, options ); + } + + return jsonml; + } + + +// merges adjacent text nodes into a single node + function merge_text_nodes( jsonml ) { + // skip the tag name and attribute hash + var i = extract_attr( jsonml ) ? 2 : 1; + + while ( i < jsonml.length ) { + // if it's a string check the next item too + if ( typeof jsonml[ i ] === "string" ) { + if ( i + 1 < jsonml.length && typeof jsonml[ i + 1 ] === "string" ) { + // merge the second string into the first and remove it + jsonml[ i ] += jsonml.splice( i + 1, 1 )[ 0 ]; + } + else { + ++i; + } + } + // if it's not a string recurse + else { + merge_text_nodes( jsonml[ i ] ); + ++i; + } + } + } + +} )( (function() { + if ( typeof exports === "undefined" ) { + window.markdown = {}; + return window.markdown; + } + else { + return exports; + } +} )() ); diff --git a/js/redirect.js b/js/redirect.js new file mode 100644 index 0000000..e4537f0 --- /dev/null +++ b/js/redirect.js @@ -0,0 +1,6 @@ +$(document).ready(function () { + // This is a hack that our old github.io URL redirects to our new 101.o.o + if(window.location.href.indexOf('github.io') > -1){ + window.location.replace('http://101.opensuse.org/'); + } +}); diff --git a/templates/gsoc.md b/templates/gsoc.md new file mode 100644 index 0000000..469c629 --- /dev/null +++ b/templates/gsoc.md @@ -0,0 +1,30 @@ +For Google Summer of Code it's necessary to write a project proposal and put it on Google Melange. +To get started, you can use this template to structure your proposal, but don't worry, you don't have to stick to this template one by one: + + **Introduction:** Your software project should solve a clearly defined problem. + Before offering the solution (your Google Summer of Code project), you should first define the problem. + What’s the current state of things? What’s the issue you wish to solve and why? + Then you should conclude with a sentence or two about your solution. This is somewhat like an elevator pitch. + + **Project goals:** This section should again be short and to the point, and it might be a good idea to format it like a list. + You should propose a clear list of deliverables, explaining exactly what you promise to do and what you do not plan to do. + “Future developments” can be mentioned, but your promise for the three months of the Google Summer of Code season is what counts. + At this point you are making a commitment. + + **Implementation:** This section can be longer and more detailed. + You should describe what you plan to do as a solution for the problem you defined earlier. + You don’t need to provide a lot of technical details, but you do need to show that you understand the technology and illustrate key technical elements of your proposed solution in reasonable detail. + + **Timeline:** This section is easily overlooked, yet it’s arguably more important than the previous section. + With the timeline you show that you understand the problem, that you have thought hard about a solution, and that you have also broken the solution down into manageable bits. + If your timeline is reasonable and its deadlines achievable, you show that you have an actual plan on how to go from idea to delivery. + With this section you set expectations, so do not make promises you can’t keep. + A modest, realistic and detailed timeline is much better than a timeline that promises to move mountains. + Mentors are often among the top professionals in their field, and they can easily spot unrealistic timelines. + + **About me:** Write a few lines about you and provide contact information. + + (Thanks to Teo Mrnjavac for the awesome [blog article](http://teom.org/blog/kde/how-to-write-a-kick-ass-proposal-for-google-summer-of-code/)) + +There are plenty of blog articles out there how to write a good GSoC proposal. +Contact your mentor early and to get to know his expectations. Just copy and paste the description from the mentoring project issue won't work! \ No newline at end of file