Change task to problem

Change task to problem

--- server/materials/data-en/level_about.coffee
+++ server/materials/data-en/level_about.coffee
@@ -10,7 +10,7 @@
                                          String.raw"""

For teachers and parents

I've already written about this in the FAQ, but just in case I'll repeat it here. I consider your children's classes here as voluntary, and I'm not going to demand anything from them, force them, etc. If a student does not want to study, or cannot force himself, I will not demand anything from him. I simply don't have any channel of influence on him.

-
But you can influence the student, so if you want there to be some result from classes, then watch how your student is engaged. On the left, in the site menu, there are "Rankings", where you can track the progress of each student. The student can also show you what tasks he tried to solve, what was the result of his attempts, etc. There is also a "Rankings by week" table, it indicates who reached what level, as well as by week who solved how many tasks. The color of the cell in the "Rankings by week" indicates how good the result was shown by the student in the next week: bright green - very good, light green — average, white — did not even try anything at all. Finally, you can always contact me.

+
But you can influence the student, so if you want there to be some result from classes, then watch how your student is engaged. On the left, in the site menu, there are "Rankings", where you can track the progress of each student. The student can also show you what problems he tried to solve, what was the result of his attempts, etc. There is also a "Rankings by week" table, it indicates who reached what level, as well as by week who solved how many problems. The color of the cell in the "Rankings by week" indicates how good the result was shown by the student in the next week: bright green - very good, light green — average, white — did not even try anything at all. Finally, you can always contact me.

A separate appeal to teachers: it often happens that strong schoolchildren are simply bored at school computer classes. If your student is engaged in this course, allow him to solve problems during the lesson, read the theory of this course! And, of course, if you have strong students — tell them about this course!

""")
@@ -24,7 +24,7 @@

This means, firstly, I will not rush you or force you to solve problems. If you want to solve, then solve, if you don't want, well, don't solve, it's easier for me — I'll have less to check. I will not stand over your soul and demand something from you.

-
Secondly, do not expect that absolutely everything that you may need to solve problems will be reflected in classes or in theoretical materials on the site. It is quite possible that in some tasks you will find that something is required that was not in theory. It's not scary. You can always ask me, but it's better to first investigate the question yourself (in your favorite book, or on the Internet), and if it doesn't work out, then ask me. I will not eat you for such questions, on the contrary, I will be very happy if you see that something additional is required in some task, and you can clearly ask what exactly you need.

+
Secondly, do not expect that absolutely everything that you may need to solve problems will be reflected in classes or in theoretical materials on the site. It is quite possible that in some problems you will find that something is required that was not in theory. It's not scary. You can always ask me, but it's better to first investigate the question yourself (in your favorite book, or on the Internet), and if it doesn't work out, then ask me. I will not eat you for such questions, on the contrary, I will be very happy if you see that something additional is required in some problem, and you can clearly ask what exactly you need.

Thirdly, if you didn't understand something in class, in my comments on your solution, in theoretical materials or anywhere else; or if you don't understand why your program doesn't work, then just contact me and ask. Do not expect that I will explain everything to you in great detail the first time or that I will write you super-detailed comments on solutions. I explain and write just with the expectation that if you do not understand something, then you will ask again.

""")

@@ -46,7 +46,7 @@
         And every single one of them is right!

         Rudyard Kipling. In the Neolithic Age

-
Each task has a lot of solutions, and many of them are correct. One should not expect that every problem has a single correct solution; there are better solutions, there are worse solutions, but there are almost always two or three significantly different solutions, which are all very good, and it is difficult to choose the best one. Be prepared for this, be prepared to see these different solutions and different approaches.

+
Each problem has a lot of solutions, and many of them are correct. One should not expect that every problem has a single correct solution; there are better solutions, there are worse solutions, but there are almost always two or three significantly different solutions, which are all very good, and it is difficult to choose the best one. Be prepared for this, be prepared to see these different solutions and different approaches.

In particular, if you have a bug in the program, then there are usually many ways to fix it. Therefore, do not be surprised if you ask me how to fix the error, and I offer you several ways to fix it.

""")

@@ -89,7 +89,7 @@

About accepting and ignoring solutions

I will review, if possible, all your solutions, and comment on them in one way or another. I will assign a new status to the successfull (OK) solutions that I review:

-
"Accepted" — the task is written more or less normally, hooray. On the page of viewing the code of your submission (by clicking the submission row) in a special tab you can see my comments. If there is no comment there, then the problem has been solved very well. If there is a comment, it usually indicates what can be improved.

+
"Accepted" — the problem is written more or less normally, hooray. On the page of viewing the code of your submission (by clicking the submission row) in a special tab you can see my comments. If there is no comment there, then the problem has been solved very well. If there is a comment, it usually indicates what can be improved.

"Ignored" — I didn't like something very much in your code. On the page of viewing the code of your submission (by clicking the submission row) in a special tab you will see my comments explaining exactly what I didn't like. Fix it and resubmit.

If you don't understand the comment, ask me, I'll explain in more detail.

@@ -100,7 +100,7 @@

Do not treat the "ignores" as a mockery of you. I will try to ignore only those programs that can be improved quite significantly (for example, by significantly reducing the amount of code). Remember that your goal is not only to write a program that will pass all the tests, but also to learn how to program in general. In fact, I put "Ignored" when I think that the comment I am writing to your submission is so important that you should definitely read it; "ignore" is a way to draw your attention to a comment.

-
The fact that you managed, even if not in the most optimal way, to write a program and pass all tests — it is very cool, but it will be even cooler if you learn and understand how the same can be done even easier. Consider that in most tasks, writing a program so that it passes all the tests is, roughly speaking, 70% success, but getting it "Accepted" is the remaining 30%.

+
The fact that you managed, even if not in the most optimal way, to write a program and pass all tests — it is very cool, but it will be even cooler if you learn and understand how the same can be done even easier. Consider that in most problems, writing a program so that it passes all the tests is, roughly speaking, 70% success, but getting it "Accepted" is the remaining 30%.

You can also view all comments (including those to the accepted submissions, and even to unsuccessful ones) in the right column on the website, and in the Comments section in the left menu.

Read more about which solutions I ignore (in Russian).

@@ -114,11 +114,11 @@

So I thought, why is algoprog worse than IT companies? And I decided to organize the possibility of some kind of analogue of internships on the algoprog. Namely, if you want, you can participate in the development of the algoprog platform itself. If you've always wanted to add some feature to the algoprog, then you can do it. Or I have a list of things for you that I've been wanting to do on algoprog for a long time, but I still don't have time, and you can do them.

-
This, of course, will be quite different from the usual tasks that you solve on the algoprog. You will have to understand the rather voluminous already existing (and very dirty) code of the algoprog, you will have to understand a programming language that is probably unfamiliar (Coffeescript, but do not be afraid — it is simple and at first very similar to python), you will have to deal with a lot of technologies that you may not have worked with yet (git, MongoDB, node.js, React, a little Redux), you will have to deal with HTML and browser Javascript, you will have to Google and read a lot - but I believe that if you absolutely confidently solve at least tasks of levels 1A and 1B, and are not afraid to figure it out yourself with the problems that arise, then you can quite deal with all this.

+
This, of course, will be quite different from the usual problems that you solve on the algoprog. You will have to understand the rather voluminous already existing (and very dirty) code of the algoprog, you will have to understand a programming language that is probably unfamiliar (Coffeescript, but do not be afraid — it is simple and at first very similar to python), you will have to deal with a lot of technologies that you may not have worked with yet (git, MongoDB, node.js, React, a little Redux), you will have to deal with HTML and browser Javascript, you will have to Google and read a lot - but I believe that if you absolutely confidently solve at least problems of levels 1A and 1B, and are not afraid to figure it out yourself with the problems that arise, then you can quite deal with all this.

The source code of the algoprog is: https://github.com/petr-kalinin/algoprog . The list of improvements that you can do is: https://github.com/petr-kalinin/algoprog/issues , or you can discuss with me and offer something of your own.

-
Unlike classic internships, I will not demand any regularity from you in your work (well, actually, as with ordinary tasks on the algoprog), and I will not pay you money :) - but I promise that I will delve into what you are doing and help. And you will contribute to the development of the algoprog, and get a lot of new diverse experiences.

+
Unlike classic internships, I will not demand any regularity from you in your work (well, actually, as with ordinary problems on the algoprog), and I will not pay you money :) - but I promise that I will delve into what you are doing and help. And you will contribute to the development of the algoprog, and get a lot of new diverse experiences.

In addition, unlike classic internships, I will not be able to pay as much attention to you as a mentor usually pays to an intern in IT companies. You will have to figure out a lot on your own, I will guide your actions rather than give specific detailed instructions. Be prepared for this.

@@ -130,14 +130,14 @@
                                                                                String.raw"""

Working with the site

Theoretical materials

Some theoretical material is presented on many topics on the site. Before you start solving problems, read the relevant theory.
-        For example, go to level 1A (inside level 1), there is a section "Arithmetic operations", and in this section there are two links — "Getting started with Python and Wing IDE" and "Tasks for arithmetic operations". Here "Getting Started with Python and Wing IDE" — this is the relevant theory (in this case, the very basics of working on python, including basic arithmetic operations). Read this theory before solving the corresponding problems. (Of course, if you know the basics of your programming language, then it makes no sense to read the most basic theory, but then you probably start with the following topics, or at least skim the theory).

+        For example, go to level 1A (inside level 1), there is a section "Arithmetic operations", and in this section there are two links — "Getting started with Python and Wing IDE" and "Problems on arithmetic operations". Here "Getting Started with Python and Wing IDE" — this is the relevant theory (in this case, the very basics of working on python, including basic arithmetic operations). Read this theory before solving the corresponding problems. (Of course, if you know the basics of your programming language, then it makes no sense to read the most basic theory, but then you probably start with the following topics, or at least skim the theory).

Problems

The main thing you will do in our classes is to solve problems. Most problems will require writing a program that reads (from the keyboard or from a file) some data, calculates new data from it, and displays the result on the screen or saves it to a file.

You can (and should) send the program you have written for verification. This site provides an opportunity to automatically check your programs — you immediately (usually within a couple of minutes) get the result of the check: your program is working correctly or not. In addition, I will see all your attempts that you send to the site, and I will be able to comment on them, write comments, etc., and you will be able to see these comments and improve your programs based on them.

Sample problem

-
Go to level 1A (inside level 1). There is a link "Problems for arithmetic operations". Go in there. You will see a list of tasks: "Pears", "Pears too", etc., up to "After and before". This is a set of problems (they say "contest" in short) that you are invited to solve in order to consolidate knowledge on the first topic of the course — arithmetic operations.
-

All the names of the problems are links, they open the actual task. Open the task "Pears". You can see her condition: "N friends have K pears. They want to divide these pears among themselves so that everyone gets as much as possible, but at the same time so that all the guys have the same number of pears. How many pears will each have?" Below is an example: when entering data "7 26", the program should output "3".

+
Go to level 1A (inside level 1). There is a link "Problems for arithmetic operations". Go in there. You will see a list of problems: "Pears", "Pears too", etc., up to "After and before". This is a set of problems (they say "contest" in short) that you are invited to solve in order to consolidate knowledge on the first topic of the course — arithmetic operations.
+

All the names of the problems are links, they open the actual problem. Open the problem "Pears". You can see her condition: "N friends have K pears. They want to divide these pears among themselves so that everyone gets as much as possible, but at the same time so that all the guys have the same number of pears. How many pears will each have?" Below is an example: when entering data "7 26", the program should output "3".

The problem condition clearly describes what your program should do: for any values of N and K, it should calculate and output the answer to the problem. In more serious problems, the condition always specifies restrictions on the permissible N and K, but in the problems for beginners on this site it is considered that N and K are "reasonable" (in particular, they are fit into int data type in C/C++); "reasonableness" in this case also includes the fact that both numbers are positive.

The example is only needed to verify that you correctly understood the statement, the format of the input data, and to help you test the program on the simplest test. However, your program should work not only on the test from the example, but also on other valid tests.

@@ -149,8 +149,8 @@

         Pay attention to the following features (it may not agree with your textbook — you need to do as it is written here, and not in the textbook. Perhaps you won't understand some of the things described here - then just skip this point).

-
We don't output any "prompts" like print("please enter N"). The task condition clearly states what exactly our program should display on the screen, and there is nothing said about the fact that it is necessary to display prompts. Therefore, any extra output to the screen will be considered as a violation of the output format.

-
There are no "fool checks", for example, there is no check "what if N = 0?", or "what if they didn't enter a number?". Similar to the previous one, this is not required in our tasks. In more serious tasks, there will be restrictions on the permissible values of N in the condition, and no one will test your program with N that does not meet these restrictions. In this problem, it is assumed that N>0 is always.

+
We don't output any "prompts" like print("please enter N"). The problem condition clearly states what exactly our program should display on the screen, and there is nothing said about the fact that it is necessary to display prompts. Therefore, any extra output to the screen will be considered as a violation of the output format.

+
There are no "fool checks", for example, there is no check "what if N = 0?", or "what if they didn't enter a number?". Similar to the previous one, this is not required in our problems. In more serious problems, there will be restrictions on the permissible values of N in the condition, and no one will test your program with N that does not meet these restrictions. In this problem, it is assumed that N>0 is always.

We don't put an empty input() (or getch(); in c++) or other delay at the end of the program. This is also not required; the information displayed on the screen is still saved and can still be viewed.

We calculated and immediately output the answer without saving it to an intermediate variable. It was possible to save:

ans = k // n
@@ -166,7 +166,7 @@

In this problem, the answer to each test is uniquely determined (for example, the answer to the example from the statement can only be 3). There are problems when there may be several correct answers for each test, in such problems the condition usually says "if there are several solutions, output any" or there is some similar phrase. In this case, your program is not obliged to give an answer that exactly matches what is specified in the example — it is enough that it is one of the correct ones. Similarly, in the future, when your program will be tested on the website (see below), any of the correct answers for each test will be allowed.

-
Come up with some more examples for this task. Try to make them diverse: so that there are different N, K, and different answers. For example, enter "5 25" and check that the answer is 5. Come up with a few more examples yourself. Important: before entering each example into the program, first calculate the answer in your mind, and only then run the program and check that it has output exactly what you expected. Try to always know the answer to your example BEFORE starting the program.

+
Come up with some more examples for this problem. Try to make them diverse: so that there are different N, K, and different answers. For example, enter "5 25" and check that the answer is 5. Come up with a few more examples yourself. Important: before entering each example into the program, first calculate the answer in your mind, and only then run the program and check that it has output exactly what you expected. Try to always know the answer to your example BEFORE starting the program.

If the program is written correctly, then it will work correctly on your tests. Then it can be submitted for verification to the site (we say "submit / send for testing to the testing system" or simply "submit to the system").

@@ -175,7 +175,7 @@

Your program is sent to the server, which checks it by running on several test cases. These test cases are unknown to you, but they are always the same in each specific problem (i.e. if you send several times, the test cases will be the same each time). So, the testing system will run your program several times, each time it will simulate keyboard input of the values N and K from the next test cases, and check that your program outputs the correct answer.

-
Under the submission form there is a table listing all your attempts (also called "submissions" or "submits") for this task. Initially, of course, there is no table there. After you have submitted your solution, it will appear in this table. The "Status" column displays the result of testing your program. Immediately after you have submitted the program, you can see there "Compiling" or "Testing", which means that your task is being tested. The table is updated automatically, wait until the final result of the check appears in the "Status" column.

+
Under the submission form there is a table listing all your attempts (also called "submissions" or "submits") for this problem. Initially, of course, there is no table there. After you have submitted your solution, it will appear in this table. The "Status" column displays the result of testing your program. Immediately after you have submitted the program, you can see there "Compiling" or "Testing", which means that your problem is being tested. The table is updated automatically, wait until the final result of the check appears in the "Status" column.

The main results of the check that you can see:

@@ -187,11 +187,11 @@

You can click on each line in the submission table, after which a window with detailed information about the submission will open. Firstly, there will be the full code of your program, so you can always check whether you have sent exactly what you wanted; if you had been solving problems from school, then when you come home, you will be able to see the codes of all your programs, etc.

-
Secondly, in the window that opens there is a tab "Protocol", where the test protocol of your program is displayed. If your program was not compiled (the status is "Compilation error"), then there will be a compilation protocol: errors found in the program will be indicated. If your program was compiled, then there will be a test protocol: for each test, information will be provided about whether this test passed (OK) or not. There are only 8 tests in the task "Pears", so there should be 8 rows in the table. If your submission has received the status "OK", then all 8 lines will be OK; otherwise, some tests will have a different status (it is usually clear from the name of the status what this means).

+
Secondly, in the window that opens there is a tab "Protocol", where the test protocol of your program is displayed. If your program was not compiled (the status is "Compilation error"), then there will be a compilation protocol: errors found in the program will be indicated. If your program was compiled, then there will be a test protocol: for each test, information will be provided about whether this test passed (OK) or not. There are only 8 tests in the problem "Pears", so there should be 8 rows in the table. If your submission has received the status "OK", then all 8 lines will be OK; otherwise, some tests will have a different status (it is usually clear from the name of the status what this means).

Thus, you can see which (by numbers) tests you have passed and which have not. But you can't find out the contents of the test (in our problem, which N and K are exactly there). This is done so that you learn to test your solution yourself and find examples on which it does not work, and not consider the testing system as a magic box that gives you examples on which your program does not work. Therefore, if your program does not work, then try to find the examples on which it is wrong. If you can't find them, then contact me, we'll try to figure it out together. As a last resort, I have access to all the test data, so if I can't find an error just by looking at your program's code, I can see what kind of test your program doesn't work on.

-
But in general, try to check the task before sending it to the system (and we will learn this separately). Treat failed attempts as a real failure, and try to make as few failed attempts as possible. In general, at many programming contests you will have only one opportunity to submit the task, and if it does not pass some tests, then you will no longer have the opportunity to improve!

+
But in general, try to check the problem before sending it to the system (and we will learn this separately). Treat failed attempts as a real failure, and try to make as few failed attempts as possible. In general, at many programming contests you will have only one opportunity to submit the problem, and if it does not pass some tests, then you will no longer have the opportunity to improve!

Accepting and ignoring submits, comments on submits

I will review all your decisions: if I have time, all submissions in general, including unsuccessful ones, or if I have little time, and there are a lot of submissions, then only submissions with the OK status.

@@ -206,57 +206,57 @@

In addition to actually writing a comment, I make a decision on each of your submissions — either I accept this submit (albeit with small comments), or I will require you to rewrite this submit taking into account my comments. In the first case, I change the status of the submission to "Accepted" (i.e. you will see exactly "Accepted" instead of "OK" in the table with your submissions), in the second case I change the status to "Ignored". Accordingly, in the first case, just take note of the comments that I wrote (and it happens that I do not write any comments), although you can rewrite the solution if you want. In the second case, be sure to rewrite the solution taking into account these comments.

(Read more about why I ignore decisions (in Russian).)

-
In the summary table, the "OK" solutions (i.e., which I haven't looked at yet) are displayed with a yellow background, the "Accepted" solutions (i.e., which I have accepted) are displayed with a green background, ignored solutions are not taken into account in the total number of tasks you have solved, just the corresponding task in the summary table is displayed with a blue background.

-
-
I will also try to write comments on your unsuccessful submissions — to point out an error or advise where to look for it, etc. Of course, I will not always write them as soon as I see your attempt, and I will not always fully report the error found — after all, it will usually be useful for you to find the error more-less independently, I will try to give you suggestive advice (but, of course, it depends on the error). In general, if you tried to do a task, but it does not pass the tests, then perhaps within a day I will write you a tip in the comments to your submission.

+
In the summary table, the "OK" solutions (i.e., which I haven't looked at yet) are displayed with a yellow background, the "Accepted" solutions (i.e., which I have accepted) are displayed with a green background, ignored solutions are not taken into account in the total number of problems you have solved, just the corresponding problem in the summary table is displayed with a blue background.

+
+
I will also try to write comments on your unsuccessful submissions — to point out an error or advise where to look for it, etc. Of course, I will not always write them as soon as I see your attempt, and I will not always fully report the error found — after all, it will usually be useful for you to find the error more-less independently, I will try to give you suggestive advice (but, of course, it depends on the error). In general, if you tried to do a problem, but it does not pass the tests, then perhaps within a day I will write you a tip in the comments to your submission.

Sometimes I may not have time to review all your unsuccessful submissions, or I do not write a comment for some other reasons. In that case, if you want to get a comment on any of them, just contact me.

Good solutions

-
After I accept your solution (I set the status "Accepted"), you have access to "good solutions" for the corresponding task — on the task page, above the form for submitting the solution, a link to "good solutions" appears.

-
-
"Good solutions" are several (up to 5) solutions that were submitted by other students of the course, and which, in my opinion, are written quite well and can be considered an example of solving this problem. Watch them, especially for those tasks where I have accepted with any comments (but in general it is useful to watch "good solutions" and for all accepted tasks in general). Compare the "good solutions" with your solution, perhaps you will see that something can be done easier, or even find other useful ideas.

+
After I accept your solution (I set the status "Accepted"), you have access to "good solutions" for the corresponding problem — on the problem page, above the form for submitting the solution, a link to "good solutions" appears.

+
+
"Good solutions" are several (up to 5) solutions that were submitted by other students of the course, and which, in my opinion, are written quite well and can be considered an example of solving this problem. Watch them, especially for those problems where I have accepted with any comments (but in general it is useful to watch "good solutions" and for all accepted problems in general). Compare the "good solutions" with your solution, perhaps you will see that something can be done easier, or even find other useful ideas.

I try to keep the balance of different languages in the "good solutions" as much as possible (i.e., as a rule, there are solutions in different languages among the "good solutions"). But look at the solutions in your language, and in other languages that you don't even know — as a rule, you can understand the algorithm even in languages that you don't know at all. On the other hand, you can also find some interesting purely linguistic subtleties and techniques for your language in "good solutions".

In addition, often in "good solutions" I show different approaches to solving the problem, often there are generally different algorithms. If you see that a "good solution" is very different from yours, it is useful to understand how it works in general.

-
At the same time, understand that in fact, hundreds of solutions have already been submitted for many tasks on the site, and in fact there are dozens of "good" ones, if not more. You see a maximum of five of them, simply because there is no point in reading dozens of more or less identical solutions. You will see some five of them which were lucky that I marked them as "good", but this "luck" is often determined by chance, well, and the principles mentioned above about the diversity of languages and approaches. That is, you don't see the top-five best solutions, but five random ones from among those that are sufficiently good.

+
At the same time, understand that in fact, hundreds of solutions have already been submitted for many problems on the site, and in fact there are dozens of "good" ones, if not more. You see a maximum of five of them, simply because there is no point in reading dozens of more or less identical solutions. You will see some five of them which were lucky that I marked them as "good", but this "luck" is often determined by chance, well, and the principles mentioned above about the diversity of languages and approaches. That is, you don't see the top-five best solutions, but five random ones from among those that are sufficiently good.

Therefore, do not assume that if your personal submit did not get into the "good" ones, then it is worse than the "good" ones. It can be just as good, or even something better than the "good" ones, it's just unlucky. If you see that the "good" solutions are written as cleanly and clearly as yours, then yours could also be "good". Conversely, if your solution is in the "good", it does not mean that it is much better than all the others :) For the same reason, the authors of "good solutions" are not signed.

On the other hand, if you have looked at the "good solutions" and think that your solution is noticeably better than all the "good" ones, write to me, maybe I will add your solution to the "good" ones. (Although I usually designate a solution as "good" at the same time as I count it, so if your solution is really noticeably better than other good ones, as a rule, I will make it "good" even before you can see "good solutions" :) .)

Rankings tables

-
All your submissions for our tasks are displayed in rankings tables, links to which are in the site menu. Namely, each cell in the main part of this table indicates the statistics of submissions from a particular student for a specific task. If there is a "+" sign in the cell, it means that the task was done successfully ("OK" or "Accepted"), if "-" — it means that the person tried to pass the task, but did not pass all the tests. The number after the icon (if any) indicates the number of unsuccessful attempts (if any). For example:
+
All your submissions for our problems are displayed in rankings tables, links to which are in the site menu. Namely, each cell in the main part of this table indicates the statistics of submissions from a particular student for a specific problem. If there is a "+" sign in the cell, it means that the problem was done successfully ("OK" or "Accepted"), if "-" — it means that the person tried to pass the problem, but did not pass all the tests. The number after the icon (if any) indicates the number of unsuccessful attempts (if any). For example:

-
An empty cell indicates that the student did not attempt on the task at all;

-
"+" indicates that the task was solved successfully on the first attempt;

+
An empty cell indicates that the student did not attempt on the problem at all;

+
"+" indicates that the problem was solved successfully on the first attempt;

notation "-" does not happen;

-
"+2" means that the task was solved on the third attempt (two unsuccessful attempts and then a successful one);

-
"-2" means that there were only two unsuccessful attempts on the task and that's it.

+
"+2" means that the problem was solved on the third attempt (two unsuccessful attempts and then a successful one);

+
"-2" means that there were only two unsuccessful attempts on the problem and that's it.

-
The background of the cell indicates the status of the task relative to the verdicts "Accepted" and "Ignored":
+
The background of the cell indicates the status of the problem relative to the verdicts "Accepted" and "Ignored":

-
Yellow background — status "OK", i.e. I haven't looked at this task yet;

+
Yellow background — status "OK", i.e. I haven't looked at this problem yet;

Green background — status "Accepted";

The blue background indicates the status "Ignored", while the ignored attempt itself is considered neither successfull nor unsuccessful.

-
The last two columns of the table indicate the total number of solved tasks and the total number of unsuccessful ("penalty") attempts (only those tasks for which the correct program was eventually obtained are taken into account, ignored attempts are not taken into account). The table is sorted by the total number of solved problems, and with an equal number of solved problems — by the number of penalty attempts.

-
-
The table also has the following feature: only students who meet two requirements at once appear in it: first, I have to activate their account, and secondly, the student must have at least one attempt to complete the task. Therefore, if you have not tried to take anything yet, do not be surprised that you are not in the table. If you have already tried to take something, but you are not in the table, then write to me - perhaps I forgot to activate you.

+
The last two columns of the table indicate the total number of solved problems and the total number of unsuccessful ("penalty") attempts (only those problems for which the correct program was eventually obtained are taken into account, ignored attempts are not taken into account). The table is sorted by the total number of solved problems, and with an equal number of solved problems — by the number of penalty attempts.

+
+
The table also has the following feature: only students who meet two requirements at once appear in it: first, I have to activate their account, and secondly, the student must have at least one attempt to complete the problem. Therefore, if you have not tried to take anything yet, do not be surprised that you are not in the table. If you have already tried to take something, but you are not in the table, then write to me - perhaps I forgot to activate you.

Similarly, problem sets (contests) they appear in the summary table only when at least someone submits some solution for some problem of this contest.

Terminology

         Above I have already introduced some specific terminology that is used in programming contests, just in case I will repeat it here:

-
A contest is any set of tasks that is somehow grouped and separated from the rest. Within the contest, the tasks are usually numbered (1, 2, 3, ... or A, B, C, ... etc.) It can be a separate round of some competition, or a set of tasks for some class or on some topic, etc. As part of our classes, we call a "contest" a set of tasks available on each individual link from a page of the appropriate level. For example, "Problems on arithmetic operations" is a separate contest.

-
A submission (synonyms: attempt, submit) is your separate attempt to submit a task to the testing system; also the program that you submitted in this attempt.

+
A contest is any set of problems that is somehow grouped and separated from the rest. Within the contest, the problems are usually numbered (1, 2, 3, ... or A, B, C, ... etc.) It can be a separate round of some competition, or a set of problems for some class or on some topic, etc. As part of our classes, we call a "contest" a set of problems available on each individual link from a page of the appropriate level. For example, "Problems on arithmetic operations" is a separate contest.

+
A submission (synonyms: attempt, submit) is your separate attempt to submit a problem to the testing system; also the program that you submitted in this attempt.

A penalty attempt (usually in the context of a summary rating) is an attempt that did not pass all the tests.

-
The test is a separate test case prepared by the authors of the task in order to test your programs on it. Usually, for each task, the author of the task prepares from 5 to 60 tests, and the programs that you submit for verification are checked in turn on all these tests. On our site, in order for the attempt to be considered successful, it is necessary that the program passes all the tests, i.e. it gives the correct answer to all the tests. (Other contests may have different rules.)

+
The test is a separate test case prepared by the authors of the problem in order to test your programs on it. Usually, for each problem, the author of the problem prepares from 5 to 60 tests, and the programs that you submit for verification are checked in turn on all these tests. On our site, in order for the attempt to be considered successful, it is necessary that the program passes all the tests, i.e. it gives the correct answer to all the tests. (Other contests may have different rules.)

""")

 module20927_7 = () ->
@@ -280,7 +280,7 @@

We will study the algorithms that underlie many such programs (we will not discuss how to make the "sort alphabetically" button, but we will discuss how to implement sorting when there is already a list of objects to sort).

Are you really going to teach?

-
Yes and no. The format of the online course implies that you will study a significant part of the material yourself. I will rather guide you, advising you where to look at this or that question, on which tasks to debug skills, and I will also comment on your programs quite actively. In addition, you can always contact me (contacts in the "About the course" section) and ask any question.

+
Yes and no. The format of the online course implies that you will study a significant part of the material yourself. I will rather guide you, advising you where to look at this or that question, on which problems to debug skills, and I will also comment on your programs quite actively. In addition, you can always contact me (contacts in the "About the course" section) and ask any question.

What is the programming language?

Generally speaking, whatever you want, of those that allow you to write console applications and are supported by this site. These are C++, Java, Python, C#, PHP, Perl, Kotlin, etc.

@@ -319,7 +319,7 @@
                                             String.raw"""
Use your head to think!

The motto of our course is Use your head to think!

-
This means that you have to think all the time when working on the course. You need to think about how to solve certain tasks, how to do a lot of other things. You will not have universal recipes that will allow you to solve all the problems — on the contrary, the best, most interesting tasks are those where you are required to come up with something.

+
This means that you have to think all the time when working on the course. You need to think about how to solve certain problems, how to do a lot of other things. You will not have universal recipes that will allow you to solve all the problems — on the contrary, the best, most interesting problems are those where you are required to come up with something.

Moreover, you should always be critical of any information you receive, of anything that is written in textbooks, of anything I tell you or that is written on the course page. Think, try to understand why it is written, why you are advised to do so, and not otherwise. In textbooks, in what other people tell you, and even more so in my materials and advice, there can easily be mistakes, typos, and finally, you may find easier ways to achieve the same — so never take for granted what you read somewhere, always think about and reflect on the information received. It may turn out that in one source you read one information, and in another source something absolutely opposite — think, experiment (see below), and understand how to do better.

@@ -339,8 +339,8 @@

Materials in which the license is not explicitly specified, but of which I am the author (this is most of the materials posted directly on the site algoprog.org ), are distributed under the Creative Commons Attribution-ShareAlike (CC BY-SA) 4.0 license.

If you are not sure which license applies to a particular material, ask me.

-
Task statements

-
Most of the the task statements are taken from the website informatics.mccme.ru , where they are mostly collected from various contests. The terms of the license for them are not clear.

+
Problem statements

+
Most of the the problem statements are taken from the website informatics.mccme.ru , where they are mostly collected from various contests. The terms of the license for them are not clear.

""", {id: "license"})

 module20927_15 = () ->
@@ -351,11 +351,11 @@

Most levels have the following structure: sublevels A and B contain new material, and sublevels C and D (the latter is not always there) — additional problems of increased complexity to consolidate this material. Sometimes in levels C and D there may be particularly difficult problems for the material of significantly earlier levels (for example, in level 5C there may be a difficult problem for the material of level 3). Sometimes there are contests on separate topics in levels C and D, sometimes additional problems in these levels go in random order.

-
I assume that you will go through the sublevels sequentially, moving to a new sublevel usually when you have "passed" the previous sublevel. Exception: if you have passed some topic in sublevel A or B, and you see a contest of advanced tasks on the same topic in sublevel C or D, then you can solve it.

-
-
The requirements for the "passing" of each sublevel are indicated under its heading. As a rule, for sublevels A and B it is required to solve all the tasks, for levels C and D it is required to solve part (half or a third) of the tasks. However, this does not mean that the remaining tasks of levels C and D do not need to be solved. I recommend that if you have solved the necessary minimum of tasks of levels C and D, go to the next level in order to quickly begin studying a new theory. But at the same time, return regularly to the unfinished tasks of the previous levels. It can be estimated approximately as follows: when you move to level 4A, you should have solved half of the tasks of level 3C, 3/4 of the tasks of level 2C and about 7/8 of the tasks of levels 1C and 1D. Similarly, when you move to level 5A, you should have solved half of the tasks of level 4C, as well as 3/4 of the tasks of level 3C, 7/8 of the tasks of level 2C and almost all (with the rare exception of particularly unpleasant tasks) tasks of levels 1C and 1D. Similarly for other levels.

-
-
Please note that in levels C and D, the tasks are not always ordered by complexity. Therefore, solve them in the order in which it is more convenient for you!

+
I assume that you will go through the sublevels sequentially, moving to a new sublevel usually when you have "passed" the previous sublevel. Exception: if you have passed some topic in sublevel A or B, and you see a contest of advanced problems on the same topic in sublevel C or D, then you can solve it.

+
+
The requirements for the "passing" of each sublevel are indicated under its heading. As a rule, for sublevels A and B it is required to solve all the problems, for levels C and D it is required to solve part (half or a third) of the problems. However, this does not mean that the remaining problems of levels C and D do not need to be solved. I recommend that if you have solved the necessary minimum of problems of levels C and D, go to the next level in order to quickly begin studying a new theory. But at the same time, return regularly to the unfinished problems of the previous levels. It can be estimated approximately as follows: when you move to level 4A, you should have solved half of the problems of level 3C, 3/4 of the problems of level 2C and about 7/8 of the problems of levels 1C and 1D. Similarly, when you move to level 5A, you should have solved half of the problems of level 4C, as well as 3/4 of the problems of level 3C, 7/8 of the problems of level 2C and almost all (with the rare exception of particularly unpleasant problems) problems of levels 1C and 1D. Similarly for other levels.

+
+
Please note that in levels C and D, the problems are not always ordered by complexity. Therefore, solve them in the order in which it is more convenient for you!

""")

@@ -374,7 +374,7 @@

Materials

-
If you do not need the functionality of the algprog, but only materials — theoretical materials, video lectures, tasks, etc. — then they are freely available in one form or another. Theoretical materials, the author of which I am, as a rule, are distributed under one or another free license, you can use them. It is usually indicated in the material itself, if the license is not specified there, then ask me. The materials that I am not the author of are taken from other sites, you can usually use them too. Almost all tasks are taken from public sites informatics.msk.ru and codeforces.com, on both of these sites you can form your courses from the tasks available there, etc. Basic (although already outdated) contents of the algoprog are on informatics: informatics.msk.ru/course/view.php?id=1135 — actually, the whole algoprog grew out of my informatics course.

+
If you do not need the functionality of the algoprog, but only materials — theoretical materials, video lectures, problems, etc. — then they are freely available in one form or another. Theoretical materials, the author of which I am, as a rule, are distributed under one or another free license, you can use them. It is usually indicated in the material itself, if the license is not specified there, then ask me. The materials that I am not the author of are taken from other sites, you can usually use them too. Almost all problems are taken from public sites informatics.msk.ru and codeforces.com, on both of these sites you can form your courses from the problems available there, etc. Basic (although already outdated) contents of the algoprog are on informatics: informatics.msk.ru/course/view.php?id=1135 — actually, the whole algoprog grew out of my informatics course.

Engine

@@ -382,7 +382,7 @@

A separate instance

-
I can set up a separate instance of the algoprog for your students, with your materials and tasks. This requires a separate discussion, but in principle it is possible. The cost will depend on the necessary work to support the instance, the necessary improvements, and the expected number of students. As a rough preliminary assessment, if no serious improvements are required, and 20-50 students are expected, then the cost of a separate instance will be about 1000 rubles per student per month; if there are fewer students, then the cost, of course, will be higher.

+
I can set up a separate instance of the algoprog for your students, with your materials and problems. This requires a separate discussion, but in principle it is possible. The cost will depend on the necessary work to support the instance, the necessary improvements, and the expected number of students. As a rough preliminary assessment, if no serious improvements are required, and 20-50 students are expected, then the cost of a separate instance will be about 1000 rubles per student per month; if there are fewer students, then the cost, of course, will be higher.

Classes directly on the algoprog based on the materials of the algoprog

@@ -439,11 +439,11 @@

The rating is determined simply by a set of solved problems. Namely, each problem costs a certain fixed number of rating points, determined by the level of this problem.

-
A Level 1A task costs $2.5$ rating points. A Level 2A task costs $2.5^2$ rating points. A level 3A task costs $2.5^3$ rating points, etc. A level $N$A task costs $2.5^N$ rating points.

-
-
The tasks of intermediate levels are worth an intermediate number of points. Namely, the task of level $N$B costs $2.5^{N+0.25}$ rating points, level $N$C — $2.5^{N+0.5}$, level $N$D — $2.5^{N+0.75}$ points.

-
-
Tasks from regional olympiads are considered as tasks from level 3A. If the same task exists at several levels, then only the highest level of this task is taken into account.

+
A Level 1A problem costs $2.5$ rating points. A Level 2A problem costs $2.5^2$ rating points. A level 3A problem costs $2.5^3$ rating points, etc. A level $N$A problem costs $2.5^N$ rating points.

+
+
The problems of intermediate levels are worth an intermediate number of points. Namely, the problem of level $N$B costs $2.5^{N+0.25}$ rating points, level $N$C — $2.5^{N+0.5}$, level $N$D — $2.5^{N+0.75}$ points.

+
+
Problems from regional olympiads are considered as problems from level 3A. If the same problem exists at several levels, then only the highest level of this problem is taken into account.

When displaying the rating in tables, it is rounded to integers, but when calculating everything is considered real numbers.

@@ -465,7 +465,7 @@

Ideology

-
The scores for the task grow very quickly, because at high levels it is not so important what you have solved at lower levels. For example, if you are already at level 5, then most likely, level 1 problems do not pose any difficulty at all for you. Therefore, it does not matter how much you have solved at level 1. And this is due to the fact that level 5 problems cost much more than level 1 problems.

+
The scores for the problem grow very quickly, because at high levels it is not so important what you have solved at lower levels. For example, if you are already at level 5, then most likely, level 1 problems do not pose any difficulty at all for you. Therefore, it does not matter how much you have solved at level 1. And this is due to the fact that level 5 problems cost much more than level 1 problems.

Activity

(The calculation code is at the same link)

@@ -502,12 +502,12 @@
     page(
         "About the \"Find a bug\" section",
                                           String.raw"""
About the "Find a bug" section

-
When you have solved a problem and received an "Accepted" for it, you have the opportunity to look for bugs in the submits of other students on this problem. Namely, a link "Find an bug" appears on the task page, which opens a list of specially selected incorrect solutions to this problem.
-        (Of course, the link does not appear in every task, but only in those tasks where there are such specially selected solutions.)
+
When you have solved a problem and received an "Accepted" for it, you have the opportunity to look for bugs in the submits of other students on this problem. Namely, a link "Find an bug" appears on the problem page, which opens a list of specially selected incorrect solutions to this problem.
+        (Of course, the link does not appear in every problem, but only in those problems where there are such specially selected solutions.)
         There is also a section in the site menu "Find a bug", where there are links to all solutions in which you can look for errors
-        (not all of these solutions are available to you — solutions are available to you only for those tasks for which you received a "Accepted").

-
-
For each of the solutions in which you need to find an error, it is known that the error can be corrected by making no more than 15 edits in the solution code. An edit is considered to be adding, deleting, or changing one character (i.e., the Levenshnein distance is actually considered). Your task is to find the error, fix it, and get OK on the task. To give you some margin, you are allowed to make up to 23 edits.

+        (not all of these solutions are available to you — solutions are available to you only for those problems for which you received a "Accepted").

+
+
For each of the solutions in which you need to find an error, it is known that the error can be corrected by making no more than 15 edits in the solution code. An edit is considered to be adding, deleting, or changing one character (i.e., the Levenshnein distance is actually considered). Your need to find the error, fix it, and get OK on the problem. To give you some margin, you are allowed to make up to 23 edits.

In the "Find a bug" interface, you are immediately given a text editor in which the solution code is open. I strongly recommend looking for errors directly in this editor, without copying the code anywhere (like your IDE or editor), and accordingly not using a debugger, etc., but only looking at the code with your eyes. But in the most difficult cases, you can copy the code and understand it more thoroughly.


Social: fix login with group invite

Social: fix login with group invite

--- servers/lib/server/handlers/jointeam.coffee
+++ servers/lib/server/handlers/jointeam.coffee
@@ -29,6 +29,8 @@
   body.emailFrequency.marketing = newsletter is 'true'
   # rename variable
   body.invitationToken          = token
+  # required for JUser.login
+  body.groupName                = slug

   return handleClientIdNotFound res, req  unless clientId


Position

Position

--- js/hocr-reader.coffee
+++ js/hocr-reader.coffee
@@ -49,6 +49,7 @@
   if page > 1
     nav_bar.append($('<a>',class:'nav_left').attr('href',"/hocr-reader/#/read/#{user}/#{repo}/1").text('First'))
     nav_bar.append($('<a>',class:'nav_left').attr('href',"/hocr-reader/#/read/#{user}/#{repo}/#{format_page(page - 1)}").text('Prev'))
+  nav_bar.append($('<span>',class:'nav_left').text("#{page} / #{total_pages}"))
   if page < total_pages
     nav_bar.append($('<a>',class:'nav_right').attr('href',"/hocr-reader/#/read/#{user}/#{repo}/#{format_page(total_pages)}").text('Last'))
     nav_bar.append($('<a>',class:'nav_right').attr('href',"/hocr-reader/#/read/#{user}/#{repo}/#{format_page(page + 1)}").text('Next'))


Fix so that file is created correctly

Fix so that file is created correctly

Initiated as an object for easier use later

--- lib/project-manager.coffee
+++ lib/project-manager.coffee
@@ -23,7 +23,7 @@

     fs.exists @file, (exists) =>
       unless exists
-        fs.open @file, 'a', (err, fd) ->
+        fs.writeFile @file, '{}', (err) ->
           # Migrate current project
           if not err and state.projectsMigrated?
             @migrate(state)


Fix the array flatten method

Fix the array flatten method

--- src/array.coffee
+++ src/array.coffee
@@ -6,7 +6,7 @@
 #	a.reduce ((xs, x) -> xs.concat flatten x if Array.isArray x else xs.concat [x]), []

 Array.prototype.flatten = () ->
-  [].concat ((if Array.isArray x then flatten x else [x]) for x in this)...
+  [].concat ((if Array.isArray x then x.flatten() else [x]) for x in this)...

 #Array.prototype.flatten = (xs) ->
 #  (x... if Array.isArray x else x) for x in xs


fix conflicts

fix conflicts

--- lib/snippets-loader.coffee
+++ lib/snippets-loader.coffee
@@ -62,7 +62,11 @@

   add: (filePath, snippetsBySelector) ->
     for selector, snippetsByName of snippetsBySelector
-      continue if selector.indexOf(@grammar.scopeName) < 0
+      # Skip snippet if it's not in the same scope or the global scope
+      if selector.indexOf(@grammar.scopeName) is -1 and
+      selector.indexOf("*") is -1
+        continue
+
       for label, snippet of snippetsByName
         snippet.label = label
         @snippets[label] = snippet


Don't call value when setting a model to a function

--- src/bacon.model.coffee
+++ src/bacon.model.coffee
@@ -34,7 +34,7 @@
     currentValue = undefined

     modificationBus = new Bacon.Bus()
-    syncBus = new Bacon.Bus()
+    syncBus         = new Bacon.Bus()

     valueWithSource = Bacon.update(
       { initial: true },
@@ -53,7 +53,7 @@
       .toProperty()

     model = valueWithSource
-      .map(".value")
+      .map((x) -> x.value)
       .skipDuplicates(isEqual)

     model.dispatcher.subscribe (event) ->


Update comment formatting

Update comment formatting

--- src/bijous.coffee
+++ src/bijous.coffee
@@ -26,13 +26,13 @@
 # Examples
 #
 #   Bijous = require 'bijous'
-#
 #   # Loads all modules
 #   bijous = new Bijous
 #   bijous.load (err, modules) ->
 #     # Access the results of module1
 #     console.log modules.module1
 #
+#   Bijous = require 'bijous'
 #   # Overrides the cwd option and loads all modules relative to it
 #   bijous = new Bijous
 #     cwd: '~/modules'
@@ -40,6 +40,7 @@
 #     # Access the results of module1
 #     console.log modules.module1
 #
+#   Bijous = require 'bijous'
 #   # Overrides the bundles option and loads all modules accordingly
 #   bijous = new Bijous
 #     bundles: 'modules/!(router)'
@@ -47,6 +48,7 @@
 #     # Access the results of module1
 #     console.log modules.module1
 #
+#   Bijous = require 'bijous'
 #   # Overrides the bundles option with multi bundles and loads all modules
 #   # one bundle's modules accordingly
 #   bijous = new Bijous


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/color.coffee
+++ src/color.coffee
@@ -39,7 +39,7 @@
     color ||= "rgba(0, 0, 0, 0)"

     if arguments.length > 2
-      alpha = if arguments[2]? then arguments[2] else 1
+      alpha = if arguments[3]? then arguments[3] else 1
       parsedColor = [arguments[0], arguments[1], arguments[2], alpha]

     parsedColor = lookup[normalizeKey(color)] || parseHex(color) || parseRGB(color)


pixie

pixie

--- src/color.coffee
+++ src/color.coffee
@@ -1,3 +1,5 @@
+test = "test"
+
 Color = (I) ->
   I ||= {}


Fix variable name to align with the rest

--- src/apm-cli.coffee
+++ src/apm-cli.coffee
@@ -106,7 +106,7 @@
             apm: apmVersion
             npm: npmVersion
             node: nodeVersion
-            atomVersion: atomVersion
+            atom: atomVersion
             python: pythonVersion
             git: gitVersion
             nodeArch: process.arch


fixed crystal path

fixed crystal path

--- src/code/crystal/config.coffee
+++ src/code/crystal/config.coffee
@@ -3,6 +3,7 @@
 extend   = require 'extend-combine'
 fs       = require 'fs'
 marked   = require 'marked'
+path     = require 'path'
 readdir  = require 'fs-readdir-recursive'
 skeemas  = require 'skeemas'
 userHome = require 'user-home'
@@ -22,37 +23,40 @@
 	cur[last] = val
 	obj

-init = (path, validate = true) ->
-	# get path
-	path = path or this.path
+init = (project_path, validate = true) ->
+	# get project path
+	project_path = project_path or this.path

-	# require path
-	if !path
-		throw new Error 'path for crystal config is required'
+	# require project path
+	if !project_path
+		throw new Error 'project_path for crystal config is required'

 	# load config
-	config = load path
+	config = load project_path

 	if !config
 		return false

+	# get crystal path
+	crystal_path = path.resolve "#{__dirname}/../../.."
+
 	if validate == false
-		return yaml.safeLoad fs.readFileSync("#{userHome}/.crystal/dev/crystal/.crystal/schema/#{config.exports.ConfigSchematic.schema}")
+		return yaml.safeLoad fs.readFileSync("#{crystal_path}/.crystal/schema/#{config.exports.ConfigSchematic.schema}")
 	else
-		config_schema = this.config "#{userHome}/.crystal/dev/crystal", false
+		config_schema = this.config crystal_path, false

 	validate = skeemas.validate config, config_schema
 	if !validate.valid
 		console.log "Configuration failed validation:"
 		console.log(validate.errors)
-		throw new Error "Invalid Configuration for path: #{path}"
+		throw new Error "Invalid Configuration for path: #{project_path}"

 	config

-load = (path) ->
+load = (project_path) ->
 	# get config
 	for ext in ['yml','yaml','cson','json','xml']
-		file = "#{path}/.crystal/config.#{ext}"
+		file = "#{project_path}/.crystal/config.#{ext}"

 		if fs.existsSync file
 			config = fs.readFileSync file


cleaned up aborts in spec/Extensions

cleaned up aborts in spec/Extensions

--- spec/Extensions.coffee
+++ spec/Extensions.coffee
@@ -153,15 +153,23 @@
         .then (c) ->
           c.abort "too ugly"

+      abortionParent = (parent) ->
+        parent.tree('abortionParent')
+        .deliver()
+        .some [abortion,abortion]
+        .else ->
+          true
+        .maybe [abortion]
+        .else ->
+          true
+        .then abortion
+        .else ->
+          true
+
       grandchild = (parent) ->
         parent.tree('child')
         .deliver()
-        .then abortion
-        .else ->
-          true
-        .some [abortion]
-        .else ->
-          true
+        .then abortionParent
         .then 'display-font', (c) ->
           c.registerAsset
             id: 'display-font-css'
@@ -171,12 +179,7 @@
       child = (parent) ->
         parent.tree('child')
         .deliver()
-        .then abortion
-        .else ->
-          true
-        .maybe [abortion]
-        .else ->
-          true
+        .then abortionParent
         .then grandchild
         .then 'ignored-font', (c) ->
           c.registerAsset
@@ -225,15 +228,23 @@
         .then (c) ->
           c.abort "too ugly"

+      abortionParent = (parent) ->
+        parent.tree('abortionParent')
+        .deliver()
+        .some [abortion,abortion]
+        .else ->
+          true
+        .maybe [abortion]
+        .else ->
+          true
+        .then abortion
+        .else ->
+          true
+
       winnergrandchild = (parent) ->
         parent.tree('winnergrandchild')
         .deliver()
-        .then abortion
-        .else ->
-          true
-        .maybe [abortion]
-        .else ->
-          true
+        .then abortionParent
         .then 'display-font', (c) ->
           c.registerAsset
             id: 'display-font-css'
@@ -243,24 +254,14 @@
       winnerchild = (parent) ->
         parent.tree('winnerchild')
         .deliver()
-        .then abortion
-        .else ->
-          true
-        .some [abortion]
-        .else ->
-          true
+        .then abortionParent
         .then winnergrandchild

       winner = (parent) ->
         parent.tree('winner')
         .deliver()
         .all [winnerchild]
-        .maybe [abortion]
-        .else ->
-          true
-        .then abortion
-        .else ->
-          true
+        .then abortionParent
         .then 'ignore-comic-sans', (c) ->
           c.registerAsset
             id: 'display-font-css'


Added tests for readonly and disabled attributes check in input/textarea elements

--- spec/forms_spec.coffee
+++ spec/forms_spec.coffee
@@ -20,6 +20,8 @@
               <input type="badtype" name="invalidtype" id="field-invalidtype" />
               <input type="text" name="email2" id="field-email2" />
               <input type="text" name="email3" id="field-email3" />
+              <input type="text" name="disabled_input_field" disabled id="disabled_input_field" />
+              <input type="text" name="readonly_input_field" readonly id="readonly_input_field" />

               Hungry
               You bet<input type="checkbox" name="hungry[]" value="you bet" id="field-hungry">
@@ -172,6 +174,18 @@
             assert.equal browser.querySelector("#field-email2").value, "headchomper@example.com"
           "should fire change event": (browser)->
             assert.ok browser.email2Changed
+
+        "disabled input can not be modified":
+          topic: (browser)->
+            browser.fill browser.querySelector("#disabled_input_field"), "yeahh"
+          "should raise error": (browser)->
+            assert.ok (browser instanceof Error)
+
+        "readonly input can not be modified":
+          topic: (browser)->
+            browser.fill browser.querySelector("#readonly_input_field"), "yeahh"
+          "should raise error": (browser)->
+            assert.ok (browser instanceof Error)

         "should callback":
           topic: (browser)->


Adds tests for word and range queries

Adds tests for word and range queries

--- spec/godef-spec.coffee
+++ spec/godef-spec.coffee
@@ -1,8 +1,8 @@
 ###
   TODO
-  - tests for word and range finding
+  - bug?: never dispose() cursor subscriptions in godef.coffee
   - bug: own-file def presents go-plus message window (empty)
-  - bug?: never dispose() cursor subscriptions in godef.coffee
+  - bug: "Uncaught TypeError: Cannot read property 'add' of null",
   - scroll target to put the def line at top of ed pane when it's in a different file?
   - how to test for dispatch of a command?
   - should I use mapMessages approach? I'm forking based on exitcode.
@@ -31,7 +31,7 @@
 _ = require ("underscore-plus")
 {Subscriber} = require 'emissary'

-describe "godef", ->
+fdescribe "godef", ->
   [editor, editorView, dispatch, filePath, workspaceElement] = []
   testText = "package main\n import \"fmt\"\n var testvar = \"stringy\"\n\nfunc f(){fmt.Println( testvar )}\n\n"

@@ -65,41 +65,45 @@
     beforeEach ->
       godef = dispatch.godef
       godef.editor = editor
+      editor.setText("foo foo.bar bar")

-    it "should return foo for |foo", ->
-      editor.setText("foo bar bar")
+    fit "should return foo for |foo", ->
       editor.setCursorBufferPosition([0,0])
       {word, range} = godef.wordAtCursor()
       expect(word).toEqual('foo')
-      expect(range).toEqual([[0,0], [0,2]])
+      expect(range).toEqual([[0,0], [0,3]])

-    it "should return foo for bar bar |foo", ->
-      editor.setText("bar bar foo")
-      editor.setCursorBufferPosition([0,8])
+    it "should return foo for fo|o", ->
+      editor.setCursorBufferPosition([0,2])
       {word, range} = godef.wordAtCursor()
       expect(word).toEqual('foo')
-      expect(range).toEqual([[0,8], [0,11]])
+      expect(range).toEqual([[0,0], [0,3]])

-    it "should return foo for fo|o", ->
-      editor.setText("foo")
-      editor.setCursorBufferPosition([0,2])
-      expect(godef.wordAtCursor()).toEqual('foo')
+    # odd that word range includes the trailing space, but cursor there
+    # isn't 'in' the word, but that's how Atom does it
+    it "should return no word for foo| foo", ->
+      editor.setCursorBufferPosition([0,3])
+      {word, range} = godef.wordAtCursor()
+      expect(word).toEqual('')
+      expect(range).toEqual([[0,3], [0,3]])

-    # arguable, but easiest to implement using atom Cursor's methods
-    it "should return empty for foo|", ->
-      editor.setText("foo")
-      editor.setCursorBufferPosition([0,3])
-      expect(godef.wordAtCursor()).toEqual('')
+    it "should return bar for |bar", ->
+      editor.setCursorBufferPosition([0,12])
+      {word, range} = godef.wordAtCursor()
+      expect(word).toEqual('bar')
+      expect(range).toEqual([[0,12], [0,15]])

     it "should return foo.bar for !foo.bar", ->
-      editor.setText("foo.bar")
-      editor.setCursorBufferPosition([0,0])
-      expect(godef.wordAtCursor()).toEqual('foo.bar')
+      editor.setCursorBufferPosition([0,4])
+      {word, range} = godef.wordAtCursor()
+      expect(word).toEqual('foo.bar')
+      expect(range).toEqual([[0,4], [0,11]])

-    it "should return foo.bar for foo.ba|r", ->
-      editor.setText("foo.bar")
-      editor.setCursorBufferPosition([0,6])
-      expect(godef.wordAtCursor()).toEqual('foo.bar')
+    fit "should return foo.bar for foo.ba|r", ->
+      editor.setCursorBufferPosition([0,10])
+      {word, range} = godef.wordAtCursor()
+      expect(word).toEqual('foo.bar')
+      expect(range).toEqual([[0,4], [0,11]])

   describe "when go-plus is loaded", ->
     it "should have registered the golang:godef command",  ->
@@ -152,7 +156,7 @@
             runs ->
               subscription.dispose()

-          fit "should create a highlight decoration of the correct class", ->
+          it "should create a highlight decoration of the correct class", ->
             done = false
             subscription = dispatch.godef.onDidComplete ->
               higlightClass = 'goplus-godef-highlight'


Add Test For Format + Vet On Save

Add Test For Format + Vet On Save

--- spec/govet-spec.coffee
+++ spec/govet-spec.coffee
@@ -49,3 +49,30 @@

       waitsFor ->
         done is true
+
+  describe "when vet on save and format on save are enabled", ->
+    beforeEach ->
+      atom.config.set("go-plus.formatOnSave", true)
+      atom.config.set("go-plus.vetOnSave", true)
+      atom.config.set("go-plus.goPath", "/usr/local/go/bin/go")
+      atom.config.set("go-plus.gofmtPath", "/usr/local/go/bin/gofmt")
+      atom.config.set("go-plus.showErrorPanel", true)
+
+    it "formats the file and displays errors for unreachable code", ->
+      done = false
+      runs ->
+        console.log 'Test File: ' + filePath
+        buffer.setText("package main\n\nimport \"fmt\"\n\nfunc main()  {\nreturn\nfmt.Println(\"Unreachable...\")}\n")
+        dispatch = atom.packages.getLoadedPackage('go-plus').mainModule.dispatch
+        dispatch.on 'dispatch-complete', =>
+          expect(fs.readFileSync(filePath, {encoding: 'utf8'})).toBe "package main\n\nimport \"fmt\"\n\nfunc main() {\n\treturn\n\tfmt.Println(\"Unreachable...\")\n}\n"
+          expect(dispatch.errorCollection?).toBe true
+          expect(_.size(dispatch.errorCollection)).toBe 1
+          expect(dispatch.errorCollection[0].column).toBe false
+          expect(dispatch.errorCollection[0].line).toBe "7"
+          expect(dispatch.errorCollection[0].msg).toBe "unreachable code"
+          done = true
+        buffer.save()
+
+      waitsFor ->
+        done is true


Add promise support.

Add promise support.

--- src/bundle.coffee
+++ src/bundle.coffee
@@ -1,7 +1,10 @@
-Module    = require './module'
-{Prelude} = require './wrapper'
-path      = require 'path'
-toRegex   = require 'to-regexp'
+Promise      = require 'broken'
+path         = require 'path'
+toRegex      = require 'to-regexp'
+
+Module       = require './module'
+{Prelude}    = require './wrapper'
+{isFunction} = require './utils'

 createWrapper = (opts) ->
   new Prelude
@@ -12,57 +15,61 @@
     preludeAsync:  opts.preludeAsync

 module.exports = (opts = {}, cb = ->) ->
-  if typeof opts == 'function'
+  if isFunction opts
     [cb, opts] = [opts, {}]

-  if opts.preludeOnly
-    unless opts.bare
-      opts.globalRequire = true
-    return cb null, createWrapper opts
+  p = new Promise (resolve, reject) ->
+    if opts.preludeOnly
+      unless opts.bare
+        opts.globalRequire = true
+      return resolve createWrapper opts

-  # Base paths
-  basePath      = opts.base ? opts.src
-  sourceMapRoot = basePath ? '/' + (path.relative process.cwd(), path.dirname opts.entry)
+    # Base paths
+    basePath      = opts.base ? opts.src
+    sourceMapRoot = basePath ? '/' + (path.relative process.cwd(), path.dirname opts.entry)

-  # Make sure we have sane exclude, include, resolveAs to pass to modules
-  exclude   = toRegex opts.exclude
-  include   = opts.include   ? {}
-  resolveAs = opts.resolveAs ? opts.resolve ? {}
+    # Make sure we have sane exclude, include, resolveAs to pass to modules
+    exclude   = toRegex opts.exclude
+    include   = opts.include   ? {}
+    resolveAs = opts.resolveAs ? opts.resolve ? {}

-  # Do not pass empty objects
-  unless (Object.keys include).length
-    include = null
-  unless (Object.keys resolveAs).length
-    resolveAs = null
+    # Do not pass empty objects
+    unless (Object.keys include).length
+      include = null
+    unless (Object.keys resolveAs).length
+      resolveAs = null

-  # Build module
-  mod = new Module opts.entry,
-    bare:          opts.bare
-    basePath:      basePath
-    compilers:     opts.compilers
-    exclude:       exclude
-    export:        opts.export
-    include:       include
-    moduleCache:   opts.moduleCache
-    paths:         opts.paths ? []
-    resolveAs:     resolveAs
-    sourceMap:     opts.sourceMap ? true
-    sourceMapRoot: opts.sourceMapRoot ? sourceMapRoot
-    strict:        opts.strict
-    urlRoot:       opts.urlRoot
+    # Build module
+    mod = new Module opts.entry,
+      bare:          opts.bare
+      basePath:      basePath
+      compilers:     opts.compilers
+      exclude:       exclude
+      export:        opts.export
+      include:       include
+      moduleCache:   opts.moduleCache
+      paths:         opts.paths ? []
+      resolveAs:     resolveAs
+      sourceMap:     opts.sourceMap ? true
+      sourceMapRoot: opts.sourceMapRoot ? sourceMapRoot
+      strict:        opts.strict
+      urlRoot:       opts.urlRoot

-  mod.parse (err) ->
-    return cb err if err?
+    mod.parse (err) ->
+      return reject err if err?

-    # Detect async
-    unless opts.async
-      for k,v of mod.moduleCache
-        if v.async
-          opts.async = true
-          break
+      # Detect async
+      unless opts.async
+        for k,v of mod.moduleCache
+          if v.async
+            opts.async = true
+            break

-    # Add wrapper
-    unless opts.bare
-      mod.toplevel = createWrapper opts
+      # Add wrapper
+      unless opts.bare
+        mod.toplevel = createWrapper opts

-    cb null, mod
+      resolve mod
+
+  p.callback cb
+  p


:lipstick: Minimap bookmark

:lipstick: Minimap bookmark


Remove EditorView from specs

Remove EditorView from specs

Signed-off-by: Max Brunsfeld <78036c9b69b887700d5846f26a788d53b201ffbb@gmail.com>

--- spec/autocomplete-spec.coffee
+++ spec/autocomplete-spec.coffee
@@ -1,4 +1,4 @@
-{EditorView, WorkspaceView} = require 'atom'
+{WorkspaceView} = require 'atom'
 {$} = require 'atom-space-pen-views'
 AutocompleteView = require '../lib/autocomplete-view'
 Autocomplete = require '../lib/autocomplete'
@@ -60,39 +60,43 @@
         expect(atom.workspaceView.getActiveView().find('.autocomplete')).not.toExist()

 describe "AutocompleteView", ->
-  [autocomplete, editorView, editor, miniEditor] = []
+  [autocomplete, editor, miniEditor] = []

   beforeEach ->
     atom.workspaceView = new WorkspaceView

     waitsForPromise ->
-      atom.workspace.open('sample.js').then (editor) ->
-        editorView = new EditorView({editor})
+      atom.workspace.open('sample.js').then (anEditor) -> editor = anEditor

     runs ->
-      {editor} = editorView
       autocomplete = new AutocompleteView(editor)
       miniEditor = autocomplete.filterEditorView

+  describe "when the view is attached to the DOM", ->
+    it "focuses the filter editor", ->
+      jasmine.attachToDOM(autocomplete.element)
+      expect(miniEditor).toHaveFocus()
+
   describe '::attach', ->
-    it "shows autocomplete view and focuses its mini-editor", ->
-      editorView.attachToDom()
-      expect(editorView.find('.autocomplete')).not.toExist()
-
-      autocomplete.attach()
-      expect(editorView.find('.autocomplete')).toExist()
-      expect(autocomplete.editor.isFocused).toBeFalsy()
-      expect(autocomplete.filterEditorView.hasFocus()).toBeTruthy()
+    it "adds the autocomplete view as an overlay decoration", ->
+      expect(editor.getOverlayDecorations().length).toBe 0
+      autocomplete.attach()
+      overlayDecorations = editor.getOverlayDecorations()
+      expect(overlayDecorations.length).toBe 1
+      expect(overlayDecorations[0].properties.item).toBe autocomplete

     describe "when the editor is empty", ->
       it "displays no matches", ->
-        editorView.attachToDom()
         editor.setText('')
-        expect(editorView.find('.autocomplete')).not.toExist()
-
-        autocomplete.attach()
+        expect(editor.getOverlayDecorations().length).toBe 0
+
+        autocomplete.attach()
+
+        overlayDecorations = editor.getOverlayDecorations()
+        expect(overlayDecorations.length).toBe 1
+        expect(overlayDecorations[0].properties.item).toBe autocomplete
+
         expect(editor.getText()).toBe ''
-        expect(editorView.find('.autocomplete')).toExist()
         expect(autocomplete.list.find('li').length).toBe 0

     describe "when no text is selected", ->
@@ -192,7 +196,7 @@
           expect(autocomplete.list.find('li:eq(1)')).toHaveText('sort')

         describe 'where text differs between cursors', ->
-          it 'cancels the autocomplete', ->
+          it 'does not display the autocomplete overlay', ->
             editor.getBuffer().insert([10,0] ,"s:extra:a")
             editor.setSelectedBufferRanges([[[10,1],[10,1]], [[10,9],[10,9]]])
             autocomplete.attach()
@@ -202,7 +206,7 @@
             expect(editor.getSelections()[0].getBufferRange()).toEqual [[10,1], [10,1]]
             expect(editor.getSelections()[1].getBufferRange()).toEqual [[10,9], [10,9]]

-            expect(editorView.find('.autocomplete')).not.toExist()
+            expect(editor.getOverlayDecorations().length).toBe 0

     describe "when text is selected", ->
       it 'autocompletes word when there is only a prefix', ->
@@ -238,7 +242,7 @@

         expect(autocomplete.list.find('li').length).toBe 0

-      it 'replaces selection with selected match, moves the cursor to the end of the match, and removes the autocomplete menu', ->
+      it 'replaces selection with selected match, moves the cursor to the end of the match, and removes the autocomplete overlay', ->
         editor.getBuffer().insert([10,0] ,"extra:sort:extra")
         editor.setSelectedBufferRange [[10,7], [10,9]]
         autocomplete.attach()
@@ -246,27 +250,18 @@
         expect(editor.lineForBufferRow(10)).toBe "extra:shift:extra"
         expect(editor.getCursorBufferPosition()).toEqual [10,11]
         expect(editor.getSelection().isEmpty()).toBeTruthy()
-        expect(editorView.find('.autocomplete')).not.toExist()
+
+        expect(editor.getOverlayDecorations().length).toBe 0

       describe "when many ranges are selected", ->
-        it 'replaces selection with selected match, moves the cursor to the end of the match, and removes the autocomplete menu', ->
+        it 'replaces selection with selected match, moves the cursor to the end of the match, and removes the autocomplete overlay', ->
           editor.getBuffer().insert([10,0] ,"sort:extra:sort")
           editor.setSelectedBufferRanges [[[10,1], [10,3]], [[10,12], [10,14]]]
           autocomplete.attach()

           expect(editor.lineForBufferRow(10)).toBe "shift:extra:shift"
           expect(editor.getSelections().length).toEqual(2)
-          expect(editorView.find('.autocomplete')).not.toExist()
-
-    describe "when the editor is scrolled to the right", ->
-      it "does not scroll it to the left", ->
-        editorView.width(300)
-        editorView.height(300)
-        editorView.attachToDom()
-        editor.setCursorBufferPosition([6, 6])
-        previousScrollLeft = editorView.scrollLeft()
-        autocomplete.attach()
-        expect(editorView.scrollLeft()).toBe previousScrollLeft
+          expect(editor.getOverlayDecorations().length).toBe 0

   describe 'core:confirm event', ->
     describe "where there are matches", ->
@@ -279,7 +274,7 @@
           expect(editor.lineForBufferRow(10)).toBe "extra:shift:extra"
           expect(editor.getCursorBufferPosition()).toEqual [10,11]
           expect(editor.getSelection().isEmpty()).toBeTruthy()
-          expect(editorView.find('.autocomplete')).not.toExist()
+          expect(editor.getOverlayDecorations().length).toBe 0

   describe 'core:cancel event', ->
     describe "when there are no matches", ->
@@ -287,6 +282,7 @@
         editor.getBuffer().insert([10,0] ,"xxx")
         editor.setCursorBufferPosition [10, 3]
         autocomplete.attach()
+        expect(editor.getOverlayDecorations().length).toBe 1
         expect(autocomplete.error).toHaveText "No matches found"

         atom.commands.dispatch miniEditor.element, "core:cancel"
@@ -294,7 +290,7 @@
         expect(editor.lineForBufferRow(10)).toBe "xxx"
         expect(editor.getCursorBufferPosition()).toEqual [10,3]
         expect(editor.getSelection().isEmpty()).toBeTruthy()
-        expect(editorView.find('.autocomplete')).not.toExist()
+        expect(editor.getOverlayDecorations().length).toBe 0

     it 'does not replace selection, removes autocomplete view and returns focus to editor', ->
       editor.getBuffer().insert([10,0] ,"extra:so:extra")
@@ -302,12 +298,13 @@
       originalSelectionBufferRange = editor.getSelection().getBufferRange()

       autocomplete.attach()
+      expect(editor.getOverlayDecorations().length).toBe 1
       editor.setCursorBufferPosition [0, 0] # even if selection changes before cancel, it should work
       atom.commands.dispatch miniEditor.element, "core:cancel"

       expect(editor.lineForBufferRow(10)).toBe "extra:so:extra"
       expect(editor.getSelection().getBufferRange()).toEqual originalSelectionBufferRange
-      expect(editorView.find('.autocomplete')).not.toExist()
+      expect(editor.getOverlayDecorations().length).toBe 0

     it "does not clear out a previously confirmed selection when canceling with an empty list", ->
       editor.getBuffer().insert([10, 0], "ort\n")
@@ -398,7 +395,8 @@

   describe "when the mini-editor receives keyboard input", ->
     beforeEach ->
-      editorView.attachToDom()
+      # List population is async, so it requires DOM attachment
+      jasmine.attachToDOM(autocomplete.element)

     describe "when text is removed from the mini-editor", ->
       it "reloads the match list based on the mini-editor's text", ->
@@ -442,6 +440,7 @@
         editor.getBuffer().insert([10,0] ,"t")
         editor.setCursorBufferPosition([10,0])
         autocomplete.attach()
+        expect(editor.getOverlayDecorations().length).toBe 1

         miniEditor.getModel().insertText('iv')
         window.advanceClock(autocomplete.inputThrottle)
@@ -449,74 +448,39 @@

         miniEditor.getModel().insertText(' ')
         window.advanceClock(autocomplete.inputThrottle)
-        expect(autocomplete.isVisible()).toBe false
+        expect(editor.getOverlayDecorations().length).toBe 0
         expect(editor.lineForBufferRow(10)).toEqual 'pivot '

-  describe 'when the mini-editor loses focus before the selection is confirmed', ->
-    it "cancels the autocomplete", ->
-      editorView.attachToDom()
-      autocomplete.attach()
-      spyOn(autocomplete, "cancel")
-
-      editorView.focus()
-
-      expect(autocomplete.cancel).toHaveBeenCalled()
-
-  describe ".attach()", ->
-    beforeEach ->
-      editorView.attachToDom()
-      setEditorHeightInLines(editorView, 13)
-      editorView.resetDisplay() # Ensures the editor only has 13 lines visible
-
-    describe "when the autocomplete view fits below the cursor", ->
-      it "adds the autocomplete view to the editor below the cursor", ->
-        editor.setCursorBufferPosition [1, 2]
-        cursorPixelPosition = editorView.pixelPositionForScreenPosition(editor.getCursorScreenPosition())
-        autocomplete.attach()
-        expect(editorView.find('.autocomplete')).toExist()
-
-        overlayElement = autocomplete.element.parentElement
-        expect(overlayElement.offsetTop).toBe cursorPixelPosition.top + editorView.lineHeight
-        expect(overlayElement.offsetLeft).toBe cursorPixelPosition.left
-
   describe ".cancel()", ->
-    it "clears the mini-editor and unbinds autocomplete event handlers for move-up and move-down", ->
+    it "removes the overlay and clears the mini editor", ->
       autocomplete.attach()
       miniEditor.setText('foo')

       autocomplete.cancel()
+      expect(editor.getOverlayDecorations().length).toBe 0
       expect(miniEditor.getText()).toBe ''

-      atom.commands.dispatch editorView.element, 'core:move-down'
-      expect(editor.getCursorBufferPosition().row).toBe 1
-
-      atom.commands.dispatch editorView.element, 'core:move-up'
-      expect(editor.getCursorBufferPosition().row).toBe 0
-
   it "sets the width of the view to be wide enough to contain the longest completion without scrolling", ->
-    editorView.attachToDom()
     editor.insertText('thisIsAReallyReallyReallyLongCompletion ')
     editor.moveCursorToBottom()
     editor.insertNewline()
     editor.insertText('t')
     autocomplete.attach()
+    jasmine.attachToDOM(autocomplete.element)
     expect(autocomplete.list.prop('scrollWidth')).toBe autocomplete.list.width()

   it "includes completions for the scope's completion preferences", ->
-    cssEditorView = null
+    cssEditor = null

     waitsForPromise ->
       atom.packages.activatePackage('language-css')

     waitsForPromise ->
-      atom.workspace.open('css.css').then (editor) ->
-        cssEditorView = new EditorView({editor})
+      atom.workspace.open('css.css').then (editor) -> cssEditor = editor

     runs ->
-      cssEditor = cssEditorView.getModel()
       autocomplete = new AutocompleteView(cssEditor)

-      cssEditorView.attachToDom()
       cssEditor.moveCursorToEndOfLine()
       cssEditor.insertText(' out')
       cssEditor.moveCursorToEndOfLine()


[rf] use coffee for-each instead of jq.each.

--- js/main.coffee
+++ js/main.coffee
@@ -2,7 +2,7 @@
 ---

 make_recent_date = () ->
-    $('#recent-list span.label').each (_, label) ->
+    for label in $('#recent-list span.label')
         seconds = (Date.now() - Date.parse($(label).data('date'))) / 1000
         day = Math.floor(seconds / 60 / 60 / 24)
         month = Math.floor(seconds / 60 / 60 / 24 / 30)
@@ -38,12 +38,12 @@
     $('#rightbar').css('left', $('#rightbar-spacing').offset()['left'])

 correct_badge_position = () ->
-    $('ul li span.badge').each (_, badge) ->
+    for badge in $('ul li span.badge')
         parent = $(badge).parent()
         $(badge).detach().prependTo(parent)

 wrap_img = () ->
-    $('img').each (_, img) ->
+    for img in $('img')
         alt = $(img).attr('alt')
         if alt?.trim().length
             $(img).wrap($('<div>').addClass('figure'))


Increase operation count in random MarkerIndex test

--- spec/marker-index-spec.coffee
+++ spec/marker-index-spec.coffee
@@ -185,7 +185,7 @@
         idCounter = 1
         markerIndex = new MarkerIndex

-        for j in [1..10]
+        for j in [1..50]
           # 80% insert, 20% delete

           if markers.length is 0 or random(10) > 2


res.end() broke sending a page after middleware finished

--- lib/HttpServer.coffee
+++ lib/HttpServer.coffee
@@ -14,15 +14,15 @@
       stack = config.get 'middle'
       next = =>
         ware = stack.shift()
-        return res.end() unless ware? # no more middleware
+        return unless ware? # no more middleware
         return next() unless ware.fn # invalid middleware
         ware.fn req, res, next, @config
       return next()
-
+
   engine: (ext, obj, data, options) -> @config.engines[ext] = compile: obj, data: data, options: options
   enable: (args...) -> @config.middle.push @config.defaultMiddle[name] for name in args when @config.defaultMiddle[name]?
   use: (name, fn, args...) -> @config.middle.push name: name, fn: fn, args: args
-
+
   listen: (@port, @host) ->
     if @config.get 'https'
       @server = https.createServer @handleRequest
@@ -31,16 +31,16 @@
     @server.listen @port, @host
     @emit 'ready'
     return @server
-
+
   close: -> @server?.close()
-
+
   handleRequest: (req, res) =>
     return unless req? and res?
     pathname = parse(req.url).pathname
     path = lookupFile pathname, @config
     req.resolvedPath = path
     @emit 'request', req, res
-
+
   loadDefaults: ->
     middleDir = join __dirname + '/middle/'
     incl = readdirSync middleDir
@@ -49,5 +49,5 @@
       if require.resolve absolute
         name = basename(file, extname(file))
         @config.defaultMiddle[name] = name: name, fn: require(absolute), args: null
-
+
 module.exports = HttpServer


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/camera.coffee
+++ src/camera.coffee
@@ -36,7 +36,7 @@
       height: 2 * deadzone.y

     if objectCenter.x > centerRect.right
-      I.scroll.x = I.scroll.x.approach(centerRect.I.maxVelocity.x)
+
     else if objectCenter.x < centerRect.left


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/camera.coffee
+++ src/camera.coffee
@@ -40,7 +40,7 @@
       I.scroll.y.clamp(centerRect.top, centerRect.bottom).clamp(I.cameraBounds.top, I.cameraBounds.bottom - I.screen.height)
     )

-    I.scroll
+    I.scroll.x = I.scro

   followTypes =
     centered: (object) ->


Fix: Can't switch tabs under iOS 5.

Fix: Can't switch tabs under iOS 5.

--- lib/ST/TabView.coffee
+++ lib/ST/TabView.coffee
@@ -18,25 +18,28 @@
     @element().empty()

     for tab, index in @_tabs
-      li = @helper().tag 'li'
+      do (index) =>
+        li = @helper().tag 'li'

-      title = tab
-      title = @helper.truncate title, @_truncateLength if @_truncateLength
-      title = String(title)
+        title = tab
+        title = @helper.truncate title, @_truncateLength if @_truncateLength
+        title = String(title)

-      if index == @_tabIndex
-        li.append '' + title + ''
-        li.addClass 'hl'
-      else
-        li.append($('' + title + '').bind((if ST.touch() then 'touchstart' else 'mousedown'), do (index) -> ->
-          self.switchToTab index
-          closePopup() if window.closePopup
-        ))
+        span = $('' + title + '')
+        li.append span

-      @_canClose = @_canClose tab, index if typeof @_canClose == 'function'
-      @helper().linkTag('X', -> self.closeTab index).addClass('close').appendTo(li) if @_canClose
+        if index == @_tabIndex
+          span.addClass 'active_title'
+          li.addClass 'hl'
+        else
+          span.addClass('inactive_title').mousedown =>
+            @switchToTab index
+            closePopup() if window.closePopup

-      @element().append li
+        @_canClose = @_canClose tab, index if typeof @_canClose == 'function'
+        @helper().linkTag('X', -> self.closeTab index).addClass('close').appendTo(li) if @_canClose
+
+        @element().append li

   @method 'closeTab', (index) ->
     tab = @_tabs[index]


added comment

added comment

--- src/core/views/internal.coffee
+++ src/core/views/internal.coffee
@@ -71,6 +71,7 @@

         # value exists? set to this view so we don't have to check
         # the parents anymore - also fixes any issue when setting a value
+        # TODO - there should be a binding somewhere here..
         if ret
           @set key, ret


Delete console.info.

Delete console.info.

--- src/Info/InfoDirective.coffee
+++ src/Info/InfoDirective.coffee
@@ -57,7 +57,6 @@
           scope.drag = false

         if scope.drag
-          console.info $event
           element[0].style.left = $event.clientX - 10 + "px"
           element[0].style.top  = $event.clientY - 10 + "px"
           $event.stopPropagation()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/bounded.coffee
+++ src/bounded.coffee
@@ -42,7 +42,8 @@

   distance: (otherObj) ->
     Point.distance(self.position(), otherObj.position())
-  ###*
+
+    ###*
   The position of this game object. By default it is the top left point.
   Redefining the center method will change the relative position.


Simplify code (by exploiting CoffeeScript a little more) in state machine event dispatching.

--- lib/connection.coffee
+++ lib/connection.coffee
@@ -212,7 +212,7 @@
       @state.enter.apply(@)

   dispatchEvent: (eventName, args...) ->
-    if @state.events && @state.events.hasOwnProperty(eventName)
+    if @state?.events[eventName]
       eventFunction = @state.events[eventName].apply(@, args)
     else
       throw new Error("No event '#{eventName}' in state '#{@state.name}'")


Firefox: some history entries have no title.

This causes `o`/`O` to crash (producing no suggestions).

As a workaround, set any such title to "".

--- background_scripts/completion.coffee
+++ background_scripts/completion.coffee
@@ -763,6 +763,8 @@
     return @callbacks.push(callback) if @callbacks
     @callbacks = [callback]
     chrome.history.search { text: "", maxResults: @size, startTime: 0 }, (history) =>
+      # On Firefox, some history entries do not have titles.
+      history.map (entry) -> entry.title ?= ""
       history.sort @compareHistoryByUrl
       @history = history
       chrome.history.onVisited.addListener(@onPageVisited.bind(this))
@@ -778,6 +780,8 @@
   # When a page we've seen before has been visited again, be sure to replace our History item so it has the
   # correct "lastVisitTime". That's crucial for ranking Vomnibar suggestions.
   onPageVisited: (newPage) ->
+    # On Firefox, some history entries do not have titles.
+    newPage.title ?= ""
     i = HistoryCache.binarySearch(newPage, @history, @compareHistoryByUrl)
     pageWasFound = (@history[i]?.url == newPage.url)
     if pageWasFound


Set activepicker within _createPicker.

Set activepicker within _createPicker.

--- src/chronos.coffee
+++ src/chronos.coffee
@@ -126,22 +126,18 @@

   # Construct a date picker and render it
   _renderPicker: ->
-    activePicker = unless @current.activePicker
-      @_createPicker()
-    else
-      @current.activePicker
+    @_createPicker() unless @current.activePicker

     # TODO: TEMPORARY
-    activePicker._renderMonths()
-    activePicker.insertAfter($(@current.displayElement))
-
-    @current.activePicker = activePicker
-    activePicker
+    @current.activePicker._renderMonths()
+    @current.activePicker.insertAfter($(@current.displayElement))
+    @current.activePicker

   # Create a new picker.
   _createPicker: ->
     picker = new chronos.Picker(@current)
+    @current.activePicker = picker
     # To handle events originating in picker which would result in the removal
     # of the picker itself.
     picker.$container.on


CLI source build fix to prevent the 'undefined' to be screwed up

--- cli/source.coffee
+++ cli/source.coffee
@@ -107,7 +107,7 @@
     module_name = options.name
     module_name+= "-#{options.version}" if options.version
     source = """
-      Lovely("#{module_name}", [#{"'#{name}'" for name in dependencies}], function(undefined) {
+      Lovely("#{module_name}", [#{"'#{name}'" for name in dependencies}], function() {
         #{source.replace(/(\n)/g, "$1  ")}

         #{if source.indexOf('var exports = {};') > -1 then "return exports;" else ""}


Document the new command

Document the new command

--- src/coffee.coffee
+++ src/coffee.coffee
@@ -13,6 +13,7 @@
 #   hubot coffee balance [user] - Check your coffee balance (defaults to you)
 #   hubot coffee top [n] - See the top coffee balances (defaults to 10)
 #   hubot coffee bottom [n] - See the bottom coffee balances (defaults to 10)
+#   hubot coffee bounty n - Issue a bounty to be claimed by the next brew
 #
 #
 # Author:


Properly stop and remove containers.

Properly stop and remove containers.

--- src/application.coffee
+++ src/application.coffee
@@ -154,7 +154,7 @@
 					knex('app').update(app).where(imageId: app.imageId)
 			# And delete all the ones to remove in one go
 			promises.push(
-				Promise.all(toBeRemoved.map(kill))
+				Promise.map(toBeRemoved, (imageId) -> kill(remoteApps[imageId]))
 				.then ->
 					knex('app').whereIn('imageId', toBeRemoved).delete()
 			)


coordinator: Make options optional

coordinator: Make options optional

--- src/coordinator.coffee
+++ src/coordinator.coffee
@@ -25,7 +25,7 @@

 class Coordinator extends EventEmitter
-  constructor: (@broker, @options) ->
+  constructor: (@broker, @options = {}) ->
     @participants = {}
     @connections = {} # connId -> { queue: opt String, handler: opt function }
     @iips = {} # iipId -> value


coordinator: Sort nodes and processes when serializing graph

Fixes #59

--- src/coordinator.coffee
+++ src/coordinator.coffee
@@ -339,12 +339,16 @@
       inports: []
       outports: []

-    for id, part of @participants
+    participantNames = Object.keys(@participants).sort()
+    for id in participantNames
+      part = @participants[id]
       name = part.role
       graph.processes[name] =
         component: part.component

-    for id, conn of @connections
+    connectionIds = Object.keys(@connections).sort()
+    for id in connectionIds
+      conn = @connections[id]
       parts = fromConnId id
       edge =
         src:


bugfix

bugfix

--- src/atom/editor.coffee
+++ src/atom/editor.coffee
@@ -59,8 +59,8 @@
   setModeForSession: (session) ->
     return if not @url

-    if not modeName = @modeFileMap[ _.last url.split '/' ]
-      language = _.last url.split '.'
+    if not modeName = @modeFileMap[ _.last @url.split '/' ]
+      language = _.last @url.split '.'
       language = language.toLowerCase()
       modeName = @modeMap[language] or language