diff --git a/db/StoredProcedures.sql b/db/StoredProcedures.sql
index d8ec988cfa791a6f745d3636b3db252c216e7af3..a3d086e2dd8291e696d8fc0e844c170f61015845 100644
--- a/db/StoredProcedures.sql
+++ b/db/StoredProcedures.sql
@@ -4043,36 +4043,46 @@ END //
 
 
 
-DROP PROCEDURE IF EXISTS GetRatesForStudentsGroup//
-CREATE PROCEDURE `GetRatesForStudentsGroup`	(	IN `TeacherID` INT,
-										IN `DisciplineID` INT,
-										IN `StudyGroupID` INT
-									)
+DROP PROCEDURE IF EXISTS GetDisciplinesForGroup//
+CREATE PROCEDURE `GetDisciplinesForGroup`	(	IN `GroupID` INT
+											)
     NO SQL
-BEGIN  				 
-    IF NOT InternalIsTeacherBinded(TeacherID, DisciplineID) 
-    THEN
-		SELECT 	NULL AS 'ID'
-		LIMIT 0;
-	ELSE
-		SELECT 	students.ID 	AS 'ID',
-				students.LastName			AS 'Last',
-				students.FirstName			AS 'First',
-				students.SecondName 		AS 'Second',
-				GetRateForDiscSemester(students.ID, DisciplineID) AS 'intermediate',
-				GetRateForDiscBonus(students.ID, DisciplineID) AS 'bonus'
-		FROM `students`
-		INNER JOIN `study_groups` ON study_groups.ID = students.StudyGroupID
-		-- LEFT JOIN `modules` ON 	modules.DisciplineID = DisciplineID AND
-		-- 						(modules.Type = 'exam' OR modules.Type = 'extra')	
-		-- LEFT JOIN `submodules` ON 	submodules.ModuleID = modules.ID
-		WHERE 	study_groups.ID = StudyGroupID AND 
-				InternalIsStudentAttached(students.ID, DisciplineID);
+BEGIN  		
+	SELECT disciplines_groups.DisciplineID
+	FROM `disciplines_groups`
+	WHERE disciplines_groups.StudyGroupID = GroupID 
+	UNION DISTINCT
+	SELECT disciplines_students.DisciplineID
+	FROM `disciplines_students`
+	INNER JOIN `students` ON disciplines_students.StudentID = students.ID
+	WHERE students.StudyGroupID = GroupID;
+
+END //
 
 
 
-		
-	END IF;
+DROP PROCEDURE IF EXISTS GetRatesForStudentsGroup//
+CREATE PROCEDURE `GetRatesForStudentsGroup`	(	IN `TeacherID` INT,
+                                                        IN `DisciplineID` INT,
+                                                        IN `StudyGroupID` INT
+                                                )
+    NO SQL
+BEGIN  		
+-- TODO: TeacherID deprecated		 
+	SELECT 	students.ID 	AS 'ID',
+			students.LastName			AS 'Last',
+			students.FirstName			AS 'First',
+			students.SecondName 		AS 'Second',
+			GetRateForDiscSemester(students.ID, DisciplineID) AS 'intermediate',
+			GetRateForDiscBonus(students.ID, DisciplineID) AS 'bonus'
+	FROM `students`
+	INNER JOIN `study_groups` ON study_groups.ID = students.StudyGroupID
+	-- LEFT JOIN `modules` ON 	modules.DisciplineID = DisciplineID AND
+	-- 						(modules.Type = 'exam' OR modules.Type = 'extra')	
+	-- LEFT JOIN `submodules` ON 	submodules.ModuleID = modules.ID
+	WHERE 	study_groups.ID = StudyGroupID AND 
+			InternalIsStudentAttached(students.ID, DisciplineID);
+
 END //
 
 
diff --git a/db/Structure.sql b/db/Structure.sql
index 91cd7209e4121d8a5f300e5074c2301f0dce3024..82ffa04a22e69da5e1efc930cbea51eb81afce91 100644
--- a/db/Structure.sql
+++ b/db/Structure.sql
@@ -513,42 +513,51 @@ CREATE TABLE IF NOT EXISTS `recovery_tokens` (
 -- Дамп данных таблицы `user_roles`
 --
 
+-- 1 - common
+-- 2 - student
+-- 4 - teacher
+-- 8 - admin
+-- 16 - deans
 INSERT INTO `user_roles` (`ID`, `Type`, `RoleName`, `Mark`) VALUES
-(1, 'student', 'Студент', 1),
-(2, 'teacher', 'Преподаватель', 2),
-(3, 'teacher', 'Преподаватель-Администратор', 4);
+(1, 'student', 'Студент', 3),
+(2, 'teacher', 'Преподаватель', 5),
+(3, 'teacher', 'Преподаватель-Администратор', 31),
+(4, 'teacher', 'Работник деканата', 21)//
 
 --
 -- Дамп данных таблицы `user_roles`
 --
 
+
+
 INSERT INTO `page_access` (`ID`, `Pagename`, `Bitmask`) VALUES
-(1, 'common:index', 7),
-(2, 'common:settings', 7),
-(3, 'common:profile', 7),
-(4, 'teacher:index', 6),
-(5, 'teacher:settings', 6),
-(6, 'teacher:map:create', 6),
-(7, 'teacher:map:edit', 6),
-(8, 'teacher:rating', 6),
-(9, 'teacher:profile', 6),
-(10, 'admin:common', 4),
-(11, 'student:index', 1),
-(12, 'student:settings', 1),
-(13, 'student:subject', 1),
-(14, 'teacher:map:discipline', 6),
-(15, 'teacher:map:structure', 6),
-(16, 'teacher:map:groups', 6),
-(17, 'teacher:map:students', 6),
-(18, 'teacher:map:teachers', 6),
-(19, 'handler:AdmAccounts', 4),
-(20, 'handler:AdmStudents', 4),
-(21, 'handler:AdmTeachers', 4),
-(22, 'handler:GetHelp', 7),
-(23, 'handler:Map', 6),
-(24, 'handler:Rating', 6),
-(25, 'handler:Settings', 7),
-(26, 'teacher:exam', 6);
+(1, 'common:index', 1),
+(2, 'common:settings', 1),
+(3, 'common:profile', 1),
+(4, 'teacher:index', 4),
+(5, 'teacher:settings', 4),
+(6, 'teacher:map:create', 4),
+(7, 'teacher:map:edit', 4),
+(8, 'teacher:rating', 4),
+(9, 'teacher:profile', 4),
+(10, 'admin:common', 8),
+(11, 'student:index', 2),
+(12, 'student:settings', 2),
+(13, 'student:subject', 2),
+(14, 'teacher:map:discipline', 4),
+(15, 'teacher:map:structure', 4),
+(16, 'teacher:map:groups', 4),
+(17, 'teacher:map:students', 4),
+(18, 'teacher:map:teachers', 4),
+(19, 'handler:AdmAccounts', 8),
+(20, 'handler:AdmStudents', 8),
+(21, 'handler:AdmTeachers', 8),
+(22, 'handler:GetHelp', 1),
+(23, 'handler:Map', 4),
+(24, 'handler:Rating', 4),
+(25, 'handler:Settings', 1),
+(26, 'teacher:exam', 4),
+(27, 'dean_office:index', 24)//
 
 
 INSERT INTO `general_settings` (`ID`, `Val`, `ValS`) VALUES
diff --git a/db/fix.sql b/db/fix.sql
index 1e7ae43aecc97782fa9cbf82ac36910c64ffddc5..b005e538021916f7a03d7adfabce57ecdbff6de1 100644
--- a/db/fix.sql
+++ b/db/fix.sql
@@ -1,237 +1,48 @@
-DELIMITER //
+DELETE FROM `page_access`;
+
+UPDATE `user_roles` SET user_roles.Mark = 31 WHERE user_roles.RoleName LIKE 'Преподаватель-Администратор';
+UPDATE `user_roles` SET user_roles.Mark = 21 WHERE user_roles.RoleName LIKE 'Работник деканата';
+UPDATE `user_roles` SET user_roles.Mark = 5 WHERE user_roles.RoleName LIKE 'Преподаватель';
+UPDATE `user_roles` SET user_roles.Mark = 3 WHERE user_roles.RoleName LIKE 'Студент';
+
+
+-- 1 - common
+-- 2 - student
+-- 4 - teacher
+-- 8 - admin
+-- 16 - deans
+-- INSERT INTO `user_roles` (`ID`, `Type`, `RoleName`, `Mark`) VALUES
+-- (1, 'student', 'Студент', 3),
+-- (2, 'teacher', 'Преподаватель', 5),
+-- (3, 'teacher', 'Преподаватель-Администратор', 31),
+-- (4, 'teacher', 'Работник деканата', 21)//
+
+INSERT INTO `page_access` (`ID`, `Pagename`, `Bitmask`) VALUES
+(1, 'common:index', 1),
+(2, 'common:settings', 1),
+(3, 'common:profile', 1),
+(4, 'teacher:index', 4),
+(5, 'teacher:settings', 4),
+(6, 'teacher:map:create', 4),
+(7, 'teacher:map:edit', 4),
+(8, 'teacher:rating', 4),
+(9, 'teacher:profile', 4),
+(10, 'admin:common', 8),
+(11, 'student:index', 2),
+(12, 'student:settings', 2),
+(13, 'student:subject', 2),
+(14, 'teacher:map:discipline', 4),
+(15, 'teacher:map:structure', 4),
+(16, 'teacher:map:groups', 4),
+(17, 'teacher:map:students', 4),
+(18, 'teacher:map:teachers', 4),
+(19, 'handler:AdmAccounts', 8),
+(20, 'handler:AdmStudents', 8),
+(21, 'handler:AdmTeachers', 8),
+(22, 'handler:GetHelp', 1),
+(23, 'handler:Map', 4),
+(24, 'handler:Rating', 4),
+(25, 'handler:Settings', 1),
+(26, 'teacher:exam', 4),
+(27, 'dean_office:index', 24);
 
-
-
-
-DROP FUNCTION IF EXISTS AddSubmodule//
-CREATE FUNCTION `AddSubmodule`  ( `TeacherID`   INT, 
-									`ModuleID`    INT, 
-									`MaxRate`     INT, 
-									`Name`      VARCHAR(200) charset utf8, 
-									`Description` VARCHAR(200) charset utf8,
-									`ControlType` VARCHAR(30) charset utf8
-								)   RETURNS int(11)
-		NO SQL
-BEGIN  
-	DECLARE checker INT;  
-
-
-	SET checker = 0;
-	SELECT MAX(submodules.OrderNum)
-	INTO checker
-	FROM `submodules`
-	WHERE   ModuleID = submodules.ModuleID
-	LIMIT 1;
-	IF checker IS NULL THEN
-		SET checker = 0;
-	END IF;
-	SET checker = checker + 1;
-
-	IF Description = "" THEN
-		INSERT INTO `submodules`  
-				(   submodules.ModuleID, submodules.MaxRate, submodules.OrderNum, submodules.Name, submodules.Description, submodules.Type )
-		VALUES  (   ModuleID, MaxRate, checker, Name, NULL, ControlType);
-	ELSE 
-		INSERT INTO `submodules`  
-				(   submodules.ModuleID, submodules.MaxRate, submodules.OrderNum, submodules.Name, submodules.Description, submodules.Type )
-		VALUES  (   ModuleID, MaxRate, checker, Name, Description, ControlType);
-	END IF;
-	
-	RETURN  ( SELECT submodules.ID
-				FROM `submodules`
-				WHERE   submodules.ModuleID = ModuleID AND
-						submodules.OrderNum = checker
-				LIMIT 1
-			);
-END //
-
-DROP FUNCTION IF EXISTS AddModuleExtra//
-CREATE FUNCTION `AddModuleExtra`	(	`TeacherID` 	INT, 
-										`DisciplineID` 	INT
-									) 	RETURNS int(11)
-    NO SQL
-BEGIN  
-    DECLARE checker, vModule, vType, vGap INT;  					 	
-    IF 	NOT InternalIsTeacherAuthor(TeacherID,DisciplineID)
-    THEN
-    	RETURN -1;
-	END IF;
-
-	SET vType = -1;
-	SET checker = -1;
-	SELECT modules.ID 
-	INTO checker
-	FROM `modules`
-	WHERE 	DisciplineID = modules.DisciplineID AND 
-			modules.Type = 4
-	LIMIT 1;
-	IF checker > 0 THEN
-		RETURN -2;
-	END IF;
-
-
-	INSERT INTO `modules` 	
-			( 	modules.Name, modules.OrderNum, modules.DisciplineID, modules.Type )
-	VALUES 	( 	'Добор баллов' , 2900666 , DisciplineID, 4 );
-
-	SET vModule = -1;
-	SELECT 	modules.ID, disciplines.ExamType 
-	INTO vModule, vType
-	FROM `modules`
-	INNER JOIN `disciplines` ON disciplines.ID = modules.DisciplineID 
-	WHERE 	DisciplineID = modules.DisciplineID AND 
-			modules.Type = 4
-	LIMIT 1;
-	IF vModule <= 0 THEN
-		RETURN -1; 
-	END IF;
-
-	SET vGap = -1;
-	IF vType = 1 THEN
-		SET vGap = 6;
-	END IF;
-	IF vType = 2 THEN
-		SET vGap = 30;
-		SET checker = AddSubmodule(TeacherID, vModule, vGap, '', NULL, 'LandmarkControl');
-	END IF; 
-
-	SET checker = AddSubmodule(TeacherID, vModule, vGap, '', NULL, 'LandmarkControl');
-	RETURN vModule;
-END //
-
-
-DROP FUNCTION IF EXISTS AddModule//
-CREATE FUNCTION `AddModule`	(	`TeacherID` 	INT, 
-								`DisciplineID` 	INT, 
-								`Name` 			VARCHAR(200) charset utf8
-							) 	RETURNS int(11)
-    NO SQL
-BEGIN  
-    DECLARE checker INT;  					 
-	
-    IF 	NOT InternalIsTeacherAuthor(TeacherID,DisciplineID)
-    THEN
-    	RETURN -1;
-	END IF;
-
-	SET checker = 0;
-	SELECT MAX(modules.OrderNum)
-	INTO checker
-	FROM `modules`
-	WHERE 	DisciplineID = modules.DisciplineID AND
-			modules.Type != 1
-	LIMIT 1;
-	IF checker IS NULL THEN
-		SET checker = 0;
-	END IF;
-	SET checker = checker + 1;
-
-
-	INSERT INTO `modules` 	
-			( 	modules.Name, modules.OrderNum, modules.DisciplineID )
-	VALUES 	(	Name , checker , DisciplineID );
-	
-
-	RETURN 	(	SELECT 	modules.ID
-				FROM 	`modules`
-				WHERE 	DisciplineID = modules.DisciplineID AND 
-						checker = modules.OrderNum
-				LIMIT 1 
-			);
-END //
-
-ALTER TABLE `modules` CHANGE `Type` `Type` enum('regular','exam', 'bonus', 'extra') NOT NULL DEFAULT 'regular'//
-
-
-DELETE FROM `rating_table`
-WHERE rating_table.SubmoduleID IN
-	(
-		SELECT submodules.ID
-		FROM `submodules`
-		INNER JOIN `modules` ON modules.ID = submodules.ModuleID
-		WHERE modules.Type = 2
-	)//
-
-
-
-INSERT INTO `page_access` (`ID`, `Pagename`, `Bitmask`) VALUES (NULL, 'teacher:exam', '6')//
-
-ALTER TABLE `specializations` ADD  `Code` varchar(12) NULL//
-
-
--- SELECT  modules.ID, AddSubmodule(disciplines.AuthorID, modules.ID, 40, '', NULL, 'LandmarkControl') AS '1'
--- FROM `modules`
--- INNER JOIN `disciplines` ON disciplines.ID = modules.DisciplineID
--- WHERE modules.Type = 2 //
-
-
-
-
-DROP PROCEDURE IF EXISTS  fix_modules//
-CREATE PROCEDURE fix_modules()
-BEGIN
-	DECLARE checker INT;
-   DECLARE vid   INT;
-   DECLARE vname INT;
-   -- this flag will be set to true when cursor reaches end of table
-   DECLARE exit_loop BOOLEAN;         
-   -- Declare the cursor
-   DECLARE employee_cursor CURSOR FOR
-   		SELECT  modules.ID AS 'ID', disciplines.AuthorID
-		FROM `modules`
-		INNER JOIN `disciplines` ON disciplines.ID = modules.DisciplineID
-		WHERE modules.Type = 2;
-
-   -- set exit_loop flag to true if there are no more rows
-   DECLARE CONTINUE HANDLER FOR NOT FOUND SET exit_loop = TRUE;
-   -- open the cursor
-   OPEN employee_cursor;
-   -- start looping
-   employee_loop: LOOP
-     -- read the name from next row into the variables 
-     FETCH  employee_cursor INTO vid, vname;
-      	SET checker = AddSubmodule(vname, vid, 40, '', NULL, 'LandmarkControl');
-      	SET checker = AddSubmodule(vname, vid, 40, '', NULL, 'LandmarkControl');
-     IF exit_loop THEN
-         CLOSE employee_cursor;
-         LEAVE employee_loop;
-     END IF;
-   END LOOP employee_loop;
-END //
-
- DROP PROCEDURE IF EXISTS  fix_modules2//
- CREATE PROCEDURE fix_modules2()
-BEGIN
-	DECLARE checker INT;
-   DECLARE vid  INT;
-   DECLARE vname INT;
-   -- this flag will be set to true when cursor reaches end of table
-   DECLARE exit_loop BOOLEAN;         
-   -- Declare the cursor
-   DECLARE employee_cursor CURSOR FOR
-   		SELECT  disciplines.ID AS 'ID', disciplines.AuthorID
-		FROM `disciplines`;
-
-   -- set exit_loop flag to true if there are no more rows
-   DECLARE CONTINUE HANDLER FOR NOT FOUND SET exit_loop = TRUE;
-   -- open the cursor
-   OPEN employee_cursor;
-   -- start looping
-   employee_loop: LOOP
-     -- read the name from next row into the variables 
-     FETCH  employee_cursor INTO vid, vname;
-     SET checker = AddModuleExtra(vname, vid);
-     IF exit_loop THEN
-         CLOSE employee_cursor;
-         LEAVE employee_loop;
-     END IF;
-   END LOOP employee_loop;
-END //
-
-
- CALL fix_modules()//
-  CALL fix_modules2()//
- DROP PROCEDURE IF EXISTS  fix_modules//
- DROP PROCEDURE IF EXISTS  fix_modules2//
-
-DELIMITER ;
\ No newline at end of file
diff --git a/status b/status
new file mode 100644
index 0000000000000000000000000000000000000000..92e178428778c8c5f9c80a629068db6595584188
--- /dev/null
+++ b/status
@@ -0,0 +1,293 @@
+commit 5a0752c941ee8fd86cd7f4de9a595b292f9771a8
+Author: pimka <pimka@ro.ru>
+Date:   Fri Dec 26 13:12:36 2014 +0300
+
+    file creator && xls
+
+commit f386533d5fd560281e6c36153e0431e6135a3620
+Author: pimka <pimka@ro.ru>
+Date:   Fri Dec 26 13:09:19 2014 +0300
+
+    modal window
+
+commit 9aac5357c8ac94c0afd050695f0052fbaae3c542
+Author: pimka <pimka@ro.ru>
+Date:   Fri Dec 26 02:27:23 2014 +0300
+
+    authentication timeout modal window
+
+commit f855afa842f0b97038f271008492594a0ab23fd9
+Merge: 853167d 1a47a4d
+Author: pimka <pimka@ro.ru>
+Date:   Thu Dec 25 23:50:15 2014 +0300
+
+    Merge branch 'master' of http://itlab.mmcs.sfedu.ru/git/grade-rating into develop
+
+commit 853167d08e2dd645524c078ab8b71101c5ceb317
+Author: PavelBegunkov <asml.Silence@gmail.com>
+Date:   Thu Dec 25 21:59:34 2014 +0300
+
+    php refactoring
+
+commit 1a47a4d0d71774ed9ec98219357aef3d7b3dc53a
+Merge: 3abe6a1 d77688d
+Author: RomanSteinberg <romanofficial@yandex.ru>
+Date:   Thu Dec 25 17:50:09 2014 +0300
+
+    Merge branch 'release/v0.9.1'
+
+commit d77688d157b6d916bb86959fd194c6bdc7e4a2f0
+Author: RomanSteinberg <romanofficial@yandex.ru>
+Date:   Thu Dec 25 17:47:13 2014 +0300
+
+    Comment rows on exam page
+
+commit 732263777a1a8cc5bcd97958ce6b8be40746d31b
+Author: RomanSteinberg <romanofficial@yandex.ru>
+Date:   Thu Dec 25 17:36:13 2014 +0300
+
+    FIX: mpdf; FIX: Select of subjects
+
+commit c976dc13553453c60d8989f2a2a946b7e5668695
+Author: RomanSteinberg <romanofficial@yandex.ru>
+Date:   Thu Dec 25 16:28:25 2014 +0300
+
+    FIX: date in excel creator
+
+commit 66b94adf8deff1b8e61e9c8102fc430918c1e48d
+Author: Антон Шалимов <solidovic@gmail.com>
+Date:   Thu Dec 25 14:28:40 2014 +0300
+
+    fix
+
+commit 365957a174b9f1e7b94ae518b04748f522dd6969
+Author: Антон Шалимов <solidovic@gmail.com>
+Date:   Thu Dec 25 14:23:44 2014 +0300
+
+    Добавил страницу для деканата (.../~dev_rating/statement)... Перейти на страницу может любой преподаватель (далее исправим)!!!
+
+commit 1ee627573167f6b76fe9814f839012e457d4ffd3
+Author: Андрей Руденец <andrey.rudenets@gmail.com>
+Date:   Wed Dec 24 15:08:44 2014 +0300
+
+    FIX: Inline-block behavior in Firefox
+
+commit acad4e77dee11e370b840181c6f61b27052864ca
+Author: Андрей Руденец <andrey.rudenets@gmail.com>
+Date:   Wed Dec 24 15:06:22 2014 +0300
+
+    IDEA support
+
+commit 98f2f648a6576d66526b94ddae6f10374ccdd00d
+Author: Антон Шалимов <solidovic@gmail.com>
+Date:   Wed Dec 24 14:35:38 2014 +0300
+
+    add <p class="help">Если вашего предмета нет в списке - пожалуйста, напишите на нам. Кнопка 'Сообщение'</p>
+
+commit ae4c5a4801dbf231076b95081c0a58bad7f5f57b
+Author: Антон Шалимов <solidovic@gmail.com>
+Date:   Tue Dec 23 23:28:36 2014 +0300
+
+    who is developer
+
+commit c24433ed93d91bd1aee085455bffbd48a5381f06
+Author: Антон Шалимов <solidovic@gmail.com>
+Date:   Tue Dec 23 23:01:48 2014 +0300
+
+    ...
+
+commit dfc569d9681f5a69c8fe36ca34884349302bb9d8
+Author: Антон Шалимов <solidovic@gmail.com>
+Date:   Tue Dec 23 22:57:15 2014 +0300
+
+    fix exam page
+
+commit 0c70cc0d8c14a5068496352fcfe6222d42374d95
+Author: PavelBegunkov <asml.Silence@gmail.com>
+Date:   Tue Dec 23 22:54:14 2014 +0300
+
+    FIX: max extra
+
+commit 20855a03a020d8b457f37fd768b67573c65ba41e
+Author: PavelBegunkov <asml.Silence@gmail.com>
+Date:   Tue Dec 23 22:49:25 2014 +0300
+
+    FIX: extra max
+
+commit 0f7b600148b65ad96a337e046a6f6a19846739af
+Merge: b79610f 3abe6a1
+Author: PavelBegunkov <asml.Silence@gmail.com>
+Date:   Tue Dec 23 22:28:39 2014 +0300
+
+    Merge branch 'master' into develop
+
+commit b79610fbf9ea53b74e7ce3c1aa3e76859f127f5f
+Author: PavelBegunkov <asml.Silence@gmail.com>
+Date:   Tue Dec 23 22:28:25 2014 +0300
+
+    style fix
+
+commit c31a73b37f3703d6d580c5c8962844d24a5c55e1
+Author: Антон Шалимов <solidovic@gmail.com>
+Date:   Tue Dec 23 21:59:23 2014 +0300
+
+    hot fix
+
+commit 3abe6a1f956c80c854c61422290dfdb48e48ce53
+Author: RomanSteinberg <romanofficial@yandex.ru>
+Date:   Tue Dec 23 15:09:26 2014 +0300
+
+    FIX: templates
+
+commit 395e76bdc369e4df68e7d5f436add5f7dc20b6ce
+Author: RomanSteinberg <romanofficial@yandex.ru>
+Date:   Tue Dec 23 14:54:04 2014 +0300
+
+    html subjects fix
+
+commit f0ca830cbeede9e18fb2dadabc64e0f55c3d7299
+Author: RomanSteinberg <romanofficial@yandex.ru>
+Date:   Tue Dec 23 14:48:12 2014 +0300
+
+    hot fixes
+
+commit 6ed52861f3f8a6a486bb3051d831f8b87116b254
+Author: RomanSteinberg <romanofficial@yandex.ru>
+Date:   Mon Dec 22 23:26:05 2014 +0300
+
+    FIX: after life testing
+
+commit 94af61dcc18b03b558577a3ba03fe09dab2e41fc
+Author: Антон Шалимов <solidovic@gmail.com>
+Date:   Mon Dec 22 21:31:41 2014 +0300
+
+    css fix
+
+commit 91669f2c2d0c9363b9f9bff199bc74550975cd85
+Author: RomanSteinberg <romanofficial@yandex.ru>
+Date:   Mon Dec 22 20:24:14 2014 +0300
+
+    UPD: update.txt
+
+commit 4c8e89e7fa3b9c73707f7951d0c1f3b3e7a5ae71
+Author: RomanSteinberg <romanofficial@yandex.ru>
+Date:   Mon Dec 22 19:59:12 2014 +0300
+
+    FIX: disabled columns
+
+commit 2e2faff2ed9fe6273c145a4c3d035615627d5062
+Author: RomanSteinberg <romanofficial@yandex.ru>
+Date:   Mon Dec 22 19:15:45 2014 +0300
+
+    fix: sort and seek
+
+commit 79597e493a49faabdd26590b623e3380e6e93824
+Author: RomanSteinberg <romanofficial@yandex.ru>
+Date:   Mon Dec 22 18:41:08 2014 +0300
+
+    FIX: { FIX: exam view; RM: rate in exam view }
+
+commit bc1f8db53ddc994dec429eef6b82ab8e9c9e5cee
+Author: RomanSteinberg <romanofficial@yandex.ru>
+Date:   Mon Dec 22 18:33:14 2014 +0300
+
+    FIX: exam view; RM: rate in exam view
+
+commit 3c8cb17ef9416526fcd0c7d09e7626c6ccf0b079
+Author: Антон Шалимов <solidovic@gmail.com>
+Date:   Mon Dec 22 12:14:59 2014 +0300
+
+    fix js hover
+
+commit 4eb2356d5d96d04b1082f19645faeba0cb3c24ad
+Author: Антон Шалимов <solidovic@gmail.com>
+Date:   Mon Dec 22 02:30:29 2014 +0300
+
+    ...
+
+commit db4d1a0d4f53a0245647f261b8f63d0ed3b077b4
+Author: Антон Шалимов <solidovic@gmail.com>
+Date:   Mon Dec 22 02:23:16 2014 +0300
+
+    ...
+
+commit 2a823db447e7e8b6558432614626fcb473133782
+Author: PavelBegunkov <asml.Silence@gmail.com>
+Date:   Mon Dec 22 01:56:27 2014 +0300
+
+    final fix
+
+commit 46a7f89e04fc2f601dc1eff3d8cd96f481ca05b3
+Author: PavelBegunkov <asml.Silence@gmail.com>
+Date:   Mon Dec 22 01:53:17 2014 +0300
+
+    fix
+
+commit 036c21f9ba5aca0860f23c34715a1ad9d45882ff
+Author: PavelBegunkov <asml.Silence@gmail.com>
+Date:   Mon Dec 22 01:42:13 2014 +0300
+
+    postfix
+
+commit e1286ba09de58aada735143833cff66c2fe9e1a7
+Author: PavelBegunkov <asml.Silence@gmail.com>
+Date:   Mon Dec 22 01:22:43 2014 +0300
+
+    fix
+
+commit fe0efb216436168b51d1454b30286455dbc7b775
+Author: PavelBegunkov <asml.Silence@gmail.com>
+Date:   Mon Dec 22 01:19:25 2014 +0300
+
+    fix
+
+commit 41051640faee74f3cc7848577957407341d18c12
+Author: PavelBegunkov <asml.Silence@gmail.com>
+Date:   Mon Dec 22 01:07:56 2014 +0300
+
+    another fix
+
+commit 7c2adecf64fd5504cdbe61fd145f737a306c70da
+Author: Антон Шалимов <solidovic@gmail.com>
+Date:   Mon Dec 22 01:06:18 2014 +0300
+
+    fix js and css
+
+commit 42d0952f00a42633517b64cbd8aa46156a70be8b
+Author: PavelBegunkov <asml.Silence@gmail.com>
+Date:   Mon Dec 22 01:02:39 2014 +0300
+
+    fix
+
+commit f6e87c15667e8d50d80bf0979f003096399edea0
+Author: PavelBegunkov <asml.Silence@gmail.com>
+Date:   Mon Dec 22 00:52:48 2014 +0300
+
+    almost dying
+
+commit 43d89cb7fd3b9ff3e2322da1735bceeaa7d468e6
+Author: Антон Шалимов <solidovic@gmail.com>
+Date:   Mon Dec 22 00:36:47 2014 +0300
+
+    css
+
+commit ac5458dd65cee56e1220c252dac19cd5df4b8ca2
+Author: PavelBegunkov <asml.Silence@gmail.com>
+Date:   Mon Dec 22 00:27:32 2014 +0300
+
+    just another fix
+
+commit c33ae701c82d5b0351a0a234195736d20570694c
+Author: PavelBegunkov <asml.Silence@gmail.com>
+Date:   Mon Dec 22 00:19:37 2014 +0300
+
+    stylish
+
+commit c0eb0f1d546e48db0a765e6e197783c7942ea8b9
+Merge: 824de16 9a0e18d
+Author: Антон Шалимов <solidovic@gmail.com>
+Date:   Mon Dec 22 00:12:00 2014 +0300
+
+    Merge branch 'develop' of http://itlab.mmcs.sfedu.ru/git/grade-rating into develop
+
+commit 9
\ No newline at end of file
diff --git a/~dev_rating/application/bootstrap.php b/~dev_rating/application/bootstrap.php
index c356dc4f0b83334ee95ec162fabd4c7577de072e..49bb2afef344e1989137c70446a2c37cbec14ff2 100644
--- a/~dev_rating/application/bootstrap.php
+++ b/~dev_rating/application/bootstrap.php
@@ -335,9 +335,9 @@ Route::set('admin:common', 'admin(/<controller>(/<action>(/<param1>(:<param2>)))
         ));
 
 /* --------------- Деканат (Ведомости) ---------------- */       
-Route::set('deans_office:statement', 'statement')
+Route::set('dean_office:index', 'dean_office')
         ->defaults(array(
-            'directory' => 'DeansOffice',
+            'directory' => 'DeanOffice',
             'controller' => 'index',
             'action' => 'index'
         ));
\ No newline at end of file
diff --git a/~dev_rating/application/classes/Controller/Authentication.php b/~dev_rating/application/classes/Controller/Authentication.php
index 54e5fcbe0df7555db31027968f4177d806e76528..1b9f8fbf3a61c95fc7602b39def0416a2c1a7572 100644
--- a/~dev_rating/application/classes/Controller/Authentication.php
+++ b/~dev_rating/application/classes/Controller/Authentication.php
@@ -4,30 +4,30 @@ class Controller_Authentication extends Controller {
         
         public function before() {
             parent::before();
-            if(UTF8::strcasecmp($this->request->action(), 'logout'))
-            {
-                if(User::instance()->isSignedIn())
-                {
-                    $page = Request::factory(User::instance()->offsetGet('Type').'/index')->execute();
+            if(UTF8::strcasecmp($this->request->action(), 'logout')) {
+                $user = User::instance();
+                if($user->isSignedIn()) {
+                    $request = $user->offsetGet('Type').'/index';
+                    $page = Request::factory($request)->execute();
                     $this->response->body($page);
                 }
             }
         }
 
 
-        protected function getUpdates() {
+        protected function getUpdates() 
+        {
             $fp = fopen(APPPATH.'updates.txt', 'r');
-                $updates['Date'] = fgets($fp, 999);
-                $updates['Text'] = '<ol>';
-                if ($fp) 
-                    {
-                    while (!feof($fp))
-                    {
-                        $updates['Text'] = $updates['Text'].'<li>'.fgets($fp, 999).'</li>';
-                    }
+            $updates['Date'] = fgets($fp, 999);
+            $text = '<ol>';
+            if ($fp) {
+                while (!feof($fp)) {
+                    $text .= '<li>'.fgets($fp, 999).'</li>';
                 }
-                $updates['Text'] = $updates['Text'].'</ol>';
-                return $updates;
+            }
+            $text .= '</ol>';
+            $updates['Text'] = $text;
+            return $updates;
         }
     
         public function action_sign()
@@ -48,7 +48,7 @@ class Controller_Authentication extends Controller {
             if(!User::instance()->isSignedIn())
             {
                 $twig = Twig::factory('sign/remindpass');
-                $twig->Updates = $twig->Updates = self::getUpdates();
+                $twig->Updates = self::getUpdates();
                 $this->response->body($twig);
             }
         }
@@ -56,9 +56,10 @@ class Controller_Authentication extends Controller {
         public function action_endremind()
         {
             $token = $this->request->param('token');
-            if(!Account::instance()->checkToken($token))
-                throw HTTP_Exception::factory (403, 
-                        'Сожалеем, но данная ссылка для восстановления пароля более недействительна!');
+            if (!Account::instance()->checkToken($token)) {
+                throw HTTP_Exception::factory(403, 
+                'Сожалеем, но данная ссылка для восстановления пароля более недействительна!');
+            }
             if(!User::instance()->isSignedIn())
             {
                 $twig = Twig::factory('sign/changepass');
diff --git a/~dev_rating/application/classes/Controller/DeansOffice/Index.php b/~dev_rating/application/classes/Controller/DeanOffice/Index.php
similarity index 72%
rename from ~dev_rating/application/classes/Controller/DeansOffice/Index.php
rename to ~dev_rating/application/classes/Controller/DeanOffice/Index.php
index 57443057136063b471fe19bcaebfd4cff8ad260e..4473e1a2e84203c4590f998877f84b9fbb870cbb 100644
--- a/~dev_rating/application/classes/Controller/DeansOffice/Index.php
+++ b/~dev_rating/application/classes/Controller/DeanOffice/Index.php
@@ -1,10 +1,10 @@
 <?php defined('SYSPATH') or die('No direct script access.');
 
-class Controller_DeansOffice_Index extends Controller_UserEnvi {
+class Controller_DeanOffice_Index extends Controller_UserEnvi {
 
     public function action_index()
     {
-        $twig = Twig::factory('DeansOffice/index');
+        $twig = Twig::factory('dean_office/index');
         $model = new Model_Teacher_Map;
 
         $twig->GradesList = DataArray::factory('Grades')->common()->asArray();
diff --git a/~dev_rating/application/classes/Controller/Handler.php b/~dev_rating/application/classes/Controller/Handler.php
index 2a03b36ee911714cdd19ab3c9b30743b5e971d77..5fb6b4461ca77e6e91d33d3089a7855bc2753ecf 100644
--- a/~dev_rating/application/classes/Controller/Handler.php
+++ b/~dev_rating/application/classes/Controller/Handler.php
@@ -11,6 +11,7 @@ class Controller_Handler extends Controller {
     public function before()
     {
         $isDownload = Cookie::get('fD');
+        $user = User::instance();
         // Если у нас запрос идет не из AJAX
         if(!$this->request->is_ajax() && !$isDownload)
         {
@@ -23,16 +24,21 @@ class Controller_Handler extends Controller {
         $this->get = Validation::factory(Arr::map('trim', $_GET));
         
         // Если авторизован, получаем данные аккаунта
-        if(User::instance()->isSignedIn())
+        if($user->isSignedIn()) 
         {
-            $this->user = User::instance()->getInfoAsArray();
+            $this->user = $user->getInfoAsArray();
         }
         
         // Получаем имя маршрута 
-        $route = Route::name($this->request->route()).':'.$this->request->controller();
-        $userMark = User::instance()->offsetGet('RoleMark');
+        $route = Route::name($this->request->route());
+        $route .= ':'.$this->request->controller();
+        $userMark = (int)$user->offsetGet('RoleMark');
+        if ($userMark == 0) {
+            $userMark = (int)1;
+        }
         // Если запрос не прошел на проверку доступа
-        if(!$this->checkAccessLevel() || !$this->checkBitmask($userMark, $route))
+        if( !$this->checkAccessLevel() || 
+            !$this->checkBitmask($userMark, $route))
         {
             // Перенаправляем на ошибку доступа
             throw HTTP_Exception::factory (403);
@@ -47,7 +53,7 @@ class Controller_Handler extends Controller {
     protected function checkBitmask($userMark, $route)
     {
         $sysModel = new Model_System;
-        $bitmask = $sysModel->getBitmaskForRoute($route);
+        $bitmask = (int)$sysModel->getBitmaskForRoute($route);
         if(!$bitmask)
             return true;
         return ($bitmask & $userMark) != 0;
@@ -70,4 +76,4 @@ class Controller_Handler extends Controller {
             break;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/~dev_rating/application/classes/Controller/Handler/FileCreator.php b/~dev_rating/application/classes/Controller/Handler/FileCreator.php
index 82901f13667dc4a5fbe270325edc2ec1f4858291..e0eb20381e10e5a74f31d0f973dd69c23c18e172 100644
--- a/~dev_rating/application/classes/Controller/Handler/FileCreator.php
+++ b/~dev_rating/application/classes/Controller/Handler/FileCreator.php
@@ -173,97 +173,212 @@ class Controller_Handler_FileCreator extends Controller_Handler
     // Ведомость
     public function action_GenerateExcelStatement()
     {
-        // TODO CHECK!!!
-
-        // parameters
+        // TODO CHECK!!! // TODO      
+		// parameters
         $disciplineID = $this->post->offsetGet('disciplineID');
         $groupID = $this->post->offsetGet('studyGroupID');
-
-        //$disciplineID = 266;
-        //$groupID = 11;
-        
-        // preparation
+		
+		// preparation
         $db = $this->model;
         $info = $db->getFinalFormInfo($disciplineID, $groupID);
+       
         $type = $info[0]['ExamType'];
-        // $specName = '';  <------- TODO
-        
         $templateFile = DOCROOT."docs/template $type.xls";
         if (!file_exists($templateFile)) {
             exit("template wasn't found" . PHP_EOL); // TODO
         }
-        
-        $result = $db->getRatesForStudentsGroup($this->user['TeacherID'], $disciplineID, $groupID);
-        $rowsCount = count($result); // Получаем количество записей 
-        
         $objPHPExcel = PHPExcel_IOFactory::load($templateFile);
         $objPHPExcel->setActiveSheetIndex(0);
-        
-    
+		        
+        // $specName = '';  <------- TODO
+		$this->printDisciplineToExcelFile($objPHPExcel, $disciplineID, $groupID, $info[0]);
+           
+        $this->GetHeaders("FinalForm".$disciplineID."_".$groupID);
+        $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5');
+        $objWriter->save('php://output');
+    } 
+	
+    public function printDisciplineToExcelFile(&$objPHPExcel, $disciplineID, $groupID, $headerData) {
+		
+        // preparation
+        $type = $headerData['ExamType'];
+        $db = $this->model;
+        $result = $db->getRatesForStudentsGroup($this->user['TeacherID'], $disciplineID, $groupID);
+		
         // fill header
+        $this->prepareSheetHeader($objPHPExcel, $type, $headerData);
+                
+        // fill students rows
+        $startRow = 13;  
+        $rowNumber = $startRow;
+        $index = 1;
+
+        foreach($result as $studentInfo){
+            $this->addStudentToSheet($objPHPExcel, $studentInfo, $rowNumber, $index);
+            $rowNumber++;
+            $index++;				
+        }    
+    }
+
+    protected function prepareSheetHeader(&$objPHPExcel, $examType, $data) {
+        $sheet = $objPHPExcel->getActiveSheet();
+        
         $range = $objPHPExcel->getNamedRange("Discipline")->getRange();
-        $objPHPExcel->getActiveSheet()->setCellValue("$range", $info[0]['SubjectName']);
-		$range = $objPHPExcel->getNamedRange("Group")->getRange();
-		$objPHPExcel->getActiveSheet()->setCellValue("$range", $info[0]['GroupNum']);
+        $sheet->setCellValue($range, $data['SubjectName']);
+        $range = $objPHPExcel->getNamedRange("Group")->getRange();
+        $sheet->setCellValue($range, $data['GroupNum']);
         $range = $objPHPExcel->getNamedRange("Subdivision")->getRange();
-        $objPHPExcel->getActiveSheet()->setCellValue("$range", $info[0]['FacultyName']);
+        $sheet->setCellValue($range, $data['FacultyName']);
         $range = $objPHPExcel->getNamedRange("Major")->getRange();  
-        $objPHPExcel->getActiveSheet()->setCellValue("$range", "Специальность: ".$info[0]['SpecName']." ".$info[0]['SpecCode']);
+        $sheet->setCellValue($range, "Специальность: ".$data['SpecName']." ".$data['SpecCode']);
         $range = $objPHPExcel->getNamedRange("Teacher")->getRange();
-        $objPHPExcel->getActiveSheet()->setCellValue("$range", $info[0]['LastName']." ".$info[0]['FirstName']." ".$info[0]['SecondName']);
+        $sheet->setCellValue($range, $data['LastName']." ".$data['FirstName']." ".$data['SecondName']);
         $range = $objPHPExcel->getNamedRange("Grade")->getRange();
-        $objPHPExcel->getActiveSheet()->setCellValue("$range", $info[0]['GradeNum']);
+        $sheet->setCellValue($range, $data['GradeNum']);
         $range = $objPHPExcel->getNamedRange("Semester")->getRange();
-        $objPHPExcel->getActiveSheet()->setCellValue("$range", $info[0]['SemesterNum']);
+        $sheet->setCellValue($range, $data['SemesterNum']);
         $range = $objPHPExcel->getNamedRange("Year")->getRange();
-        $objPHPExcel->getActiveSheet()->setCellValue("$range", $info[0]['Year']);
+        $sheet->setCellValue($range, $data['Year']);
         $range = $objPHPExcel->getNamedRange("CreationDate")->getRange();
-        $objPHPExcel->getActiveSheet()->setCellValue("$range", date("d.m.y"));
+        $sheet->setCellValue($range, date("d.m.y"));
         $range = $objPHPExcel->getNamedRange("Date")->getRange();
-        if ($type == 'exam') 
-            $controlDate = "Дата экзамена \n__________";
-        else
-            $controlDate = "Дата зачета \n30.12.2014";
-        $objPHPExcel->getActiveSheet()->setCellValue("$range", $controlDate);
+        if ($examType == 'exam') {
+            $controlDate = '__________';
+        } else {
+            $controlDate = '30.12.2014';
+        }
+        $sheet->setCellValue("$range", "Дата зачета \n$controlDate");
+    }
     
-        // fill students rows
-        $startRow = 12; 
-        $finalRow = 52; 
-        $indPosition = 'A'; // Номер 
-        $namePosition = 'B';  // ФИО 
-        $totalRatePosition = 'G';  // Итоговый рейтинг 
-        $semesterRatePosition  = 'H';   // Сумма баллов
-        $bonusRatePosition = 'I'; // Бонусные баллы
-        $rowNumber = $startRow;
-        $index = 1;
-        foreach($result as $studentInfo){
-            
-            $lastName = $studentInfo['Last'];
-            $firstName = $studentInfo['First'];
-            $secondName = $studentInfo['Second'];
-            $rate = (int)$studentInfo['intermediate'];
-            $bonus = (int)$studentInfo['bonus'];
-            $fullName = "$lastName  $firstName $secondName";
+    protected function addStudentToSheet(&$objPHPExcel, $data, $row, $index) 
+    {
+            $sheet = $objPHPExcel->getActiveSheet();
+        
+            $indPosition = 'A'; // Номер 
+            $namePosition = 'B';  // ФИО 
+            $totalRatePosition = 'G';  // Итоговый рейтинг 
+            $semesterRatePosition  = 'H';   // Сумма баллов
+            $bonusRatePosition = 'I'; // Бонусные баллы
+        
+            $sheet  ->getStyle("A".$row.":S".$row)
+                    ->getBorders()->getAllBorders()
+                    ->setBorderStyle(PHPExcel_Style_Border::BORDER_THIN);
+            $sheet  ->getStyle("G".$row.":S".$row)
+                    ->getAlignment()
+                    ->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
+            $sheet->mergeCells("B".$row.":F".$row);
+			
+            $lastName = $data['Last'];
+            $firstName = $data['First'];
+            $secondName = $data['Second'];
+            $rate = (int)$data['intermediate'];
+            $bonus = (int)$data['bonus'];
+            $fullName = $lastName." ".$firstName." ".$secondName;
             $totalRate = $rate + $bonus;
-       
-            $objPHPExcel->getActiveSheet()->setCellValue("$indPosition$rowNumber", "$index")
-                                  ->setCellValue("$namePosition$rowNumber", "$fullName")
-                                  ->setCellValue("$totalRatePosition$rowNumber", "$totalRate")
-                                  ->setCellValue("$semesterRatePosition$rowNumber","$rate")
-                                  ->setCellValue("$bonusRatePosition$rowNumber","$bonus");
-            $rowNumber++;
-            $index++;
-        }    
+			
+            if($totalRate < 60) {
+                    $tempRate = " ";
+                    $tempStr = "не зачтено";
+            } else {
+                    $tempStr = "зачтено";
+                    $tempRate = $totalRate;
+            }
+			
+            $sheet  ->setCellValue($indPosition.$row, $index)
+                    ->setCellValue($namePosition.$row, $fullName)
+                    ->setCellValue($totalRatePosition.$row, $tempRate)
+                    ->setCellValue($semesterRatePosition.$row, $rate)
+                    ->setCellValue($bonusRatePosition.$row, $bonus)
+                    ->setCellValue("J".$row, $tempStr);
+        
+    }
+	
+    protected function getSheetName(&$disciplineInfo) {
+        $str = $disciplineInfo['SubjectAbbr'];
+        if (empty($str)) {
+            $str = $disciplineInfo['SubjectName'];
+        }
+        //$str = preg_replace('/[^A-Za-z0-9\-]/', '', $str);
+        return substr($str, 0, 10);
+    }
+	
+    public function action_GenerateFinalFormsForGroup() {
+        // parameters
+        //$gradeID = $this->post->offsetGet('gradeID');
+        $groupID = $this->post->offsetGet('GroupID');
 
-        // delete rest rows
-        $objPHPExcel->getActiveSheet()->removeRow($rowNumber+1, $finalRow);
+        // preparation
+        $db = $this->model;
+        $listDisciplines = $db->GetDisciplinesForGroup($groupID);
 
-        $this->GetHeaders('FinalForm');
+        $templateFile = DOCROOT."docs/template credit.xls";
+        if (!file_exists($templateFile)) {
+                exit("template wasn't found" . PHP_EOL); // TODO
+        }
+        $objPHPExcel = PHPExcel_IOFactory::load($templateFile);
+        // Make copy of sheet
+        $sheetTemplate = $objPHPExcel->getActiveSheet()->copy();
+        // Make copy of named ranges
+        $nmrange = $objPHPExcel->getNamedRanges() ; 
+
+        $index = 0;
+        foreach ($listDisciplines as $record) {
+                $disciplineID = $record['DisciplineID'];
+                $info = $db->getFinalFormInfo($disciplineID, $groupID);
+
+                $type = $info[0]['ExamType'];
+                if ($type == 'exam') {
+                continue;
+            }
 
+            if ($index > 0) {
+                        // remove name ranges from active page
+                        foreach ($objPHPExcel->getNamedRanges() as $name => $r )
+                        {
+                                $objPHPExcel -> removeNamedRange ($r->getName(), null);
+                        }	
+                        //make clone from page copy and include it in workbook
+                        $C = clone $sheetTemplate;
+                        try {
+                            $C->setTitle($this->getSheetName($info[0]));
+                        } catch (Exception $ex) {
+                            $C->setTitle("Без_имени_".$index);
+                        }  
+                       
+                        $objPHPExcel->addSheet($C,$index);
+                        $objPHPExcel->setActiveSheetIndex($index);
+                        // create name ranges at new sheet 
+                        foreach ($nmrange as $namedRange) {
+                                $name = $namedRange->getName();
+                                $rg = $namedRange->getRange();
+                                $objPHPExcel->addNamedRange(new PHPExcel_NamedRange($name, $objPHPExcel->getActiveSheet(),$rg));
+                        }
+                }
+                else
+                {   
+                    try {
+                        $objPHPExcel->setActiveSheetIndex($index)->setTitle($this->getSheetName($info[0]));
+                    } catch (Exception $ex) {
+                        $objPHPExcel->setActiveSheetIndex($index)->setTitle("Без_имени_".$index);
+                    }        
+                    $gradeNum = $info[0]['GradeNum'];
+                    $groupNum = $info[0]['GroupNum'];
+                }
+
+                $this->printDisciplineToExcelFile($objPHPExcel, $disciplineID, $groupID, $info[0]);
+                $index++;
+        }
+        
+        if ($index === 0) {
+            throw HTTP_Exception::factory(400);
+            return;
+        }
+
+        $this->GetHeaders("FinalForm_".$gradeNum."_".$groupNum);
         $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5');
         $objWriter->save('php://output');
     }
-
+	
     // Выводим HTTP-заголовки
     public function GetHeaders($filename = 'excel')
     {
diff --git a/~dev_rating/application/classes/Controller/Handler/Session.php b/~dev_rating/application/classes/Controller/Handler/Session.php
index 7604fbfd3ad950dfc24e4659e116c1f29f85308b..09e7b24f3f0307e64c9be2a8b9e19d5f65afadb1 100644
--- a/~dev_rating/application/classes/Controller/Handler/Session.php
+++ b/~dev_rating/application/classes/Controller/Handler/Session.php
@@ -2,28 +2,35 @@
 
 class Controller_Handler_Session extends Controller_Handler {
 
-        protected $_session;
+        protected $_user;
         
         public function before() {
-            $this->setAccessLevel(self::ACCESS_ANYBODY);
+            $this->_user = User::instance(true);
             parent::before();
-            $this->_session = Session::instance();
+            $this->setAccessLevel(self::ACCESS_ANYBODY);
         }
 
         public function action_getSessionTime() {
-
-            if (User::instance()->isSignedIn() && $this->_session) {
-                $dif_time = $this->_session->get('dif_time');
-                $timeout = User::SESSION_LIFETIME;
-                $remain = $timeout - $dif_time;
-                $this->_session->set('last_time', 
-                            $this->_session->get('last_time') - $dif_time);
-            } else {
+            $user = $this->_user;
+            $session = Session::instance();
+            
+            if (!$user->isSignedIn()) {
                 $remain = 0;
+            } else {
+                $start_time = $session->get('start_time');
+                if (!isset($start_time)) {
+                    $start_time =  time();
+                }
+                $interval = time() - $start_time;
+                $config = Kohana::$config->load('session');
+                $lifetime = $config["native"]["lifetime"];
+                $remain = $lifetime - $interval; 
             }
             $this->response->body(json_encode($remain));
         }
 
+        
+        
         public function action_closeSession() {
             if (User::instance()->isSignedIn()) {
                 User::instance()->signOut();
diff --git a/~dev_rating/application/classes/Controller/Student/Index.php b/~dev_rating/application/classes/Controller/Student/Index.php
index c3ba87ecf0ebd562ae4431597866d0f149c56ebd..aa7fb553d088c3d82d0e4186daacceb96476eda8 100644
--- a/~dev_rating/application/classes/Controller/Student/Index.php
+++ b/~dev_rating/application/classes/Controller/Student/Index.php
@@ -13,7 +13,6 @@ class Controller_Student_Index extends Controller_UserEnvi {
         foreach($disciplines as $row)
         {
             $i++;
-            $color = 0;
             $disciplinesHandled[$i]['ID'] = $row['ID'];
             if($row['ExamType'] == 'exam')
             {
@@ -73,7 +72,7 @@ class Controller_Student_Index extends Controller_UserEnvi {
                 $color = 5;
             elseif($percent >= 0.85 AND $percent <= 0.94)
                 $color = 6;
-            elseif($percent >= 0.95 AND $percent <= 1)
+            elseif($percent >= 0.95)
                 $color = 7;
             if($examRate !== NULL AND $examRate < 22)
                 $color = 2;
diff --git a/~dev_rating/application/classes/Controller/Teacher/Rating.php b/~dev_rating/application/classes/Controller/Teacher/Rating.php
index 514d80cbf01dd89c10472c22b1c0c023172cbc46..dea08e8edd8902165e030c2abafe429efa5245f7 100644
--- a/~dev_rating/application/classes/Controller/Teacher/Rating.php
+++ b/~dev_rating/application/classes/Controller/Teacher/Rating.php
@@ -3,48 +3,65 @@
 class Controller_Teacher_Rating extends Controller_UserEnvi {
 
     public function before() {
-        Cookie::set('fD', 'true'); // Без куки файл качаться не будет, типа защита
+        Cookie::set('fD', 'true'); // Ставим кук fD, чтоб иметь возможность скачать отчет TODO
+
+        $this->model_rating = new Model_Teacher_Rating;
+        $this->model_discipline = new Model_Teacher_Map;
+
         parent::before();
     }
-    
-    // Получить из кука SGID выбранную ранее группу
-    private function GetSGID($id)
+
+    // Получить из кука SGID выбранную ранее группу для данной дисциплины
+    private function getStudyGroupID_ForFilter($id)
     {
         # $id = disciplineID
-        $SG_ar = json_decode(Cookie::get('SGID', null), true);
-        if ($SG_ar !== null) 
+        $SG_array = json_decode(Cookie::get('SGID', null), true);
+        if ($SG_array !== null) 
         {
-            if (array_key_exists($id, $SG_ar))
-                return $SG_ar[$id];
+            if (array_key_exists($id, $SG_array))
+                return $SG_array[$id];
         }
         return 0;
     }
 
+    // Настройки дисциплины и выбранная группа(для фильтра)
+    private function getDisciplineInformation($id) {
+
+        $temp = $this->model_discipline->getDisciplineInfoByID($id);
+        $disciplineInfo['ExamType'] = $temp[0]['ExamType'];
+        $disciplineInfo['ID'] = $id;
+        $disciplineInfo['StudyGroupID_Filter'] = $this->getStudyGroupID_ForFilter($id);
+
+        return $disciplineInfo;
+    }
+
+
+    // Страница оценивания в течение семестра
     public function action_edit()
     {
         $twig = Twig::factory('teacher/rating');
         $twig->User = $this->UserInfo;
         $id = $this->request->param('id');
-        $db = new Model_Teacher_Rating;
-		
+
         // Шапка таблицы: структура УКД (модули и мероприятия)
-		$structure = $db->GetMapForDiscipline($this->UserInfo['TeacherID'], $id);
+		$structure = $this->model_rating->GetMapForDiscipline($this->UserInfo['TeacherID'], $id);
 
         if($structure->count() == 0)
             throw HTTP_Exception::factory (404, "Для дисциплины с ID $id не создана УКД или такой дисциплины не существует!");
         
 		$structureHandled = array();
-        $maxRate = 0; $i = 0; $module = 0;
+        $maxRate = 0; $i = 0; $temp_moduleID = 0;
 
         foreach($structure as $row)
         {
-            if($row['ModuleID'] != $module)
+            if($row['ModuleID'] != $temp_moduleID)
             {
-                $i++;
-                $module = $row['ModuleID'];
+                $i++; // todo
+                $temp_moduleID = $row['ModuleID'];
             }
             if ($i == 1)
-                    $twig->SubjectName = $row['SubjectName'];
+                $twig->SubjectName = $row['SubjectName'];
+
             if(!isset($structureHandled[$i]['SubmodulesCount']))
             {
                 $structureHandled[$i]['SubmodulesCount'] = 0;
@@ -70,14 +87,11 @@ class Controller_Teacher_Rating extends Controller_UserEnvi {
         }
         $structureHandled['ModulesCount'] = $i;
         $structureHandled['MaxRate'] = (int) $maxRate; 
-
-        $twig->headerRate = $structureHandled; 
-        //echo debug::vars($structureHandled);
         // end Шапка таблицы
 
 
         // Студенты и их баллы
-        $students = $db->GetStudentsForRating($this->UserInfo['TeacherID'], $id);
+        $students = $this->model_rating->GetStudentsForRating($this->UserInfo['TeacherID'], $id);
 
         $rateHandled = array();
         $groupsHandled = array();
@@ -85,7 +99,8 @@ class Controller_Teacher_Rating extends Controller_UserEnvi {
 
         foreach($students as $row) {
             // Группа
-            if ($curGroup !== $row['GroupID']) {
+            if ($curGroup !== $row['GroupID']) 
+            {
                 $curGroup = $row['GroupID'];
                 $i_g++;
                 $rateHandled[$i_g]['GroupID'] = $row['GroupID'];
@@ -104,7 +119,37 @@ class Controller_Teacher_Rating extends Controller_UserEnvi {
 
                 $groupsHandled[$curGroup] = $rateHandled[$i_g]['GradeTitle']." ".$row['GroupNum']." РіСЂСѓРїРїР°"; 
             }
+
             // Студенты
+            $curStudent = array();
+            $i_s++;
+            $curStudent['ID'] = $row['ID'];
+            $curStudent['Last'] = $row['Last'];
+            $curStudent['First'] = $row['First'];
+            $curStudent['Second'] = $row['Second'];
+            $curStudent['isAttached'] = $row['isAttached']; // #1
+
+            // Баллы студента
+            $rates = $this->model_rating->getMapForStudent($row['ID'], $id);
+            $i_r = 0;
+            $curStudent['RateResult'] = 0;
+            foreach($rates as $r) {
+                $i_r++;
+                $curStudent['Rates'][$i_r]['Rate'] = $r['Rate'];
+                $curStudent['RateResult'] += $r['Rate'];
+
+                if ($r['ModuleType'] == 'exam')
+                    $curStudent['Rates'][$i_r]['SubmoduleID'] = -1;
+                else if ($r['ModuleType'] == 'extra')
+                    $curStudent['Rates'][$i_r]['SubmoduleID'] = -2;
+                else 
+                    $curStudent['Rates'][$i_r]['SubmoduleID'] = $r['SubmoduleID'];
+
+                $curStudent['Rates'][$i_r]['Type'] = $r['ModuleType'];
+            }
+            $rateHandled[$i_g]['Students'][$i_s] = $curStudent;
+
+            /*
             $i_s++;
             $rateHandled[$i_g]['Students'][$i_s]['ID'] = $row['ID'];
             $rateHandled[$i_g]['Students'][$i_s]['Last'] = $row['Last'];
@@ -113,7 +158,7 @@ class Controller_Teacher_Rating extends Controller_UserEnvi {
             $rateHandled[$i_g]['Students'][$i_s]['isAttached'] = $row['isAttached']; // #1
 
             // Баллы студента
-            $rate = $db->getMapForStudent($row['ID'], $id);
+            $rate = $this->model_rating->getMapForStudent($row['ID'], $id);
             $i_r = 0;
             $rateHandled[$i_g]['Students'][$i_s]['RateResult'] = 0;
             foreach($rate as $r) {
@@ -128,53 +173,49 @@ class Controller_Teacher_Rating extends Controller_UserEnvi {
                 else 
                     $rateHandled[$i_g]['Students'][$i_s]['Rates'][$i_r]['SubmoduleID'] = $r['SubmoduleID'];
             }
+            */
         }
 
+        // На вывод
+        $twig->headerRate = $structureHandled; 
         $twig->rateTable = $rateHandled;
         $twig->groups = $groupsHandled;
 
-        $twig->studyGroupID = $this->GetSGID($id);
-
-        $twig->DisciplineID = $id;
+        $twig->disciplineInfo = $this->getDisciplineInformation($id);
+        $twig->disciplineInfo_JSON = json_encode($twig->disciplineInfo);
 
 		$this->response->body($twig);
     }
 
+    // Страница оценивания в сессию
     public function action_exam()
     {
         $twig = Twig::factory('teacher/exam');
         $twig->User = $this->UserInfo;
         $id = $this->request->param('id');
-        $db = new Model_Teacher_Rating;
-
-        // Узнаем ExamType TODO
-        $dbMap = new Model_Teacher_Map;
-        $disc_temp = $dbMap->getDisciplineInfoByID($id);
-        $disciplineInfo['ExamType'] = $disc_temp[0]['ExamType'];
-        $disciplineInfo['ID'] = $id;
-        // END TODO
 
         // Шапка таблицы: структура УКД (модули и мероприятия)
-        $structure = $db->GetMapForDisciplineExam($this->UserInfo['TeacherID'], $id);
+        $structure = $this->model_rating->GetMapForDisciplineExam($this->UserInfo['TeacherID'], $id);
 
         if($structure->count() == 0)
             throw HTTP_Exception::factory (404, "Страница не найдена");
 
         
         $structureHandled = array();
-        $maxRate = 0; $i = 0; $module = 0; $try_1 = 0; // try = 1 - экзамен, = 2, 3 - пересдачи
+        $maxRate = 0; $i = 0; $temp_moduleID = 0; $try_1 = 0; // try = 1 - экзамен, = 2, 3 - пересдачи
         $examRate = 0;
         $try = 0;
 
         foreach($structure as $row)
         {
-            if($row['ModuleID'] != $module)
+            if($row['ModuleID'] != $temp_moduleID)
             {
-               ++$i;
-               $module = $row['ModuleID'];
+               ++$i; // todo
+               $temp_moduleID = $row['ModuleID'];
             }
             if ($i == 1)
-                  $twig->SubjectName = $row['SubjectName'];
+                $twig->SubjectName = $row['SubjectName'];
+
             if(!isset($structureHandled[$i]['SubmodulesCount']))
             {
                 $structureHandled[$i]['SubmodulesCount'] = 0;
@@ -206,12 +247,10 @@ class Controller_Teacher_Rating extends Controller_UserEnvi {
         }
         $structureHandled['ModulesCount'] = $i;
         $structureHandled['MaxRate'] = (int) $maxRate; 
-
-        $twig->headerRate = $structureHandled;
         // end Шапка таблицы
 
         // Студенты и их баллы по экзамену
-        $students = $db->GetStudentsForRating($this->UserInfo['TeacherID'], $id);
+        $students = $this->model_rating->GetStudentsForRating($this->UserInfo['TeacherID'], $id);
 
         $rateHandled = array();
         $groupsHandled = array();
@@ -241,9 +280,8 @@ class Controller_Teacher_Rating extends Controller_UserEnvi {
                 $groupsHandled[$curGroupID] = $rateHandled[$i_g]['GradeTitle']." ".$row['GroupNum']." РіСЂСѓРїРїР°"; 
                 $i_s = 0;
             }
-            // Студенты
-            
-            
+
+            // Студенты   
             $curStudent = array();
             $curStudent['ID'] = $row['ID'];
             $curStudent['Last'] = $row['Last'];
@@ -256,7 +294,7 @@ class Controller_Teacher_Rating extends Controller_UserEnvi {
             $i_r = 0;
             $examRate = 0;
             $extraNum = 0;
-            $rate = $db->getMapForStudentExam($row['ID'], $id);
+            $rate = $this->model_rating->getMapForStudentExam($row['ID'], $id);
 
             $lastExam = -1;
             $lastExtra = -1;
@@ -304,7 +342,7 @@ class Controller_Teacher_Rating extends Controller_UserEnvi {
 
 
             $curStudent['RateResult'] += $examRate;
-            $total = $db->GetStudentRate($row['ID'], $id);
+            $total = $this->model_rating->GetStudentRate($row['ID'], $id);
             $total = $total[0]['Num'];
             if (is_null($total)) $total = 0;
             
@@ -334,14 +372,13 @@ class Controller_Teacher_Rating extends Controller_UserEnvi {
             $i_s++;
         }
 
-        //echo Debug::vars($structureHandled);
-        //echo Debug::vars($rateHandled);	
+        // На вывод
+	    $twig->headerRate = $structureHandled;
         $twig->rateTable = $rateHandled;
         $twig->groups = $groupsHandled;
 
-        $twig->studyGroupID = $this->GetSGID($id);
-
-        $twig->disciplineInfo = $disciplineInfo;
+        $twig->disciplineInfo = $this->getDisciplineInformation($id);
+        $twig->disciplineInfo_JSON = json_encode($twig->disciplineInfo);
 
         $this->response->body($twig);
     }
diff --git a/~dev_rating/application/classes/Controller/Twig.php b/~dev_rating/application/classes/Controller/Twig.php
index d1f2614b755e2753c6f324a50c5abdaa29165ac0..231cc71368275e961d96b9c19b6fd6ff3318af2a 100644
--- a/~dev_rating/application/classes/Controller/Twig.php
+++ b/~dev_rating/application/classes/Controller/Twig.php
@@ -4,8 +4,8 @@ class Controller_Twig extends Controller {
         
         public function action_show()
         {
-                $path = $this->request->param('id');
-                $path = UTF8::str_ireplace(':', '/', $path);
+                $id = $this->request->param('id');
+                $path = UTF8::str_ireplace(':', '/', $id);
                 if(Kohana::find_file('views', $path, 'twig'))
                 {
                     $twig = Twig::factory($path);
diff --git a/~dev_rating/application/classes/Controller/UserEnvi.php b/~dev_rating/application/classes/Controller/UserEnvi.php
index 095f2601a2b1ffb29b87ef7c19bf8937cee79e46..f91d3bbcee26391ce0cc0d8a53ac30c445ae042d 100644
--- a/~dev_rating/application/classes/Controller/UserEnvi.php
+++ b/~dev_rating/application/classes/Controller/UserEnvi.php
@@ -2,54 +2,54 @@
  
 class Controller_UserEnvi extends Controller {
     protected $UserInfo;
+    protected static $degrees = array(  'bachelor' => 'Бакалавриат', 
+                                        'specialist' => 'Специалитет', 
+                                        'master' => 'Магистратура');
     
+        
     public function before()
     {
-        if(!User::instance()->isSignedIn())
-        {
+        $user = User::instance();
+        if(!$user->isSignedIn()) {
             $this->redirect('sign', 302);
+            return;
+        } 
+        $this->UserInfo = $user->getInfoAsArray();
+        if($this->UserInfo['Type'] == 'student') {
+            //unified degree from db
+            $uniDegree = $this->UserInfo['Degree']; 
+            $this->UserInfo['Degree'] = self::$degrees[$uniDegree];
         }
-        else 
-        {
-            
-            // Проверка на 
-            $this->UserInfo = User::instance()->getInfoAsArray();
-            if($this->UserInfo['Type'] == 'student')
-            {
-                $degrees = array('bachelor' => 'Бакалавриат', 'specialist' => 'Специалитет', 'master' => 'Магистратура');
-                $this->UserInfo['Degree'] = $degrees[$this->UserInfo['Degree']];
-            }
-            // Проверка на доступ к странице
-            $route = Route::name($this->request->route());
-            $userMark = User::instance()->offsetGet('RoleMark');
-            $sysModel = new Model_System;
-            $bitmask = $sysModel->getBitmaskForRoute($route);
-            if(!($bitmask & $userMark))
-            {
-                throw HTTP_Exception::factory(403, 'Не пытайтесь попасть туда, куда попадать не следует.');
-            }
+        // Проверка на доступ к странице
+        $route = Route::name($this->request->route());
+        $userMark = $user->offsetGet('RoleMark');
+        $sysModel = new Model_System;
+        $bitmask = (int)$sysModel->getBitmaskForRoute($route);
+        if ($bitmask === 0) {
+            $bitmask = (int)1;
+        }
+        if ($userMark === 0) {
+            $userMark = (int)1;
+        }
+        if(!($bitmask & $userMark)) {
+            throw HTTP_Exception::factory(403, 
+            'Не пытайтесь попасть туда, куда попадать не следует.');
         }
     }
     
     public function action_index()
     {
-        $page = Request::factory($this->UserInfo['Type'].'/index')->execute();
+        $type = $this->UserInfo['Type'];
+        $page = Request::factory($type.'/index')->execute();
         $this->response->body($page);
     }
     
     public function action_profile()
     {
-        if($this->UserInfo['Type'] != 'teacher')
-        {
+        if($this->UserInfo['Type'] != 'teacher') {
             $this->redirect('/', 302);
-        }
-        else
-        {
+        } else {
             $url = "teacher/profile";
-            if(!empty($type))
-                $url .= '/'.$type;
-            if(!empty($id))
-                $url .= '/'.$id;
             $page = Request::factory($url)->execute();
             $this->response->body($page);
         }
diff --git a/~dev_rating/application/classes/Model/Teacher/Rating.php b/~dev_rating/application/classes/Model/Teacher/Rating.php
index a57687d68f1e2239e9def1887eca760a2ece377e..02c4c36b2db40b96dc9b1b300a8bb7cda53510eb 100644
--- a/~dev_rating/application/classes/Model/Teacher/Rating.php
+++ b/~dev_rating/application/classes/Model/Teacher/Rating.php
@@ -61,4 +61,10 @@ class Model_Teacher_Rating extends Model
         return DB::query(Database::SELECT, $sql)->execute();
     }
     
+    public function getDisciplinesForGroup($groupID)
+    {
+        $sql = "CALL `GetDisciplinesForGroup`($groupID)";
+        return DB::query(Database::SELECT, $sql)->execute();
+    }
+    
 }
diff --git a/~dev_rating/application/views/base.twig b/~dev_rating/application/views/base.twig
index 224775bdb30862dcfb0c963f581de39b6f91e655..baf3a73d842d6f18f6236c804fbcf01a7ec16d8f 100644
--- a/~dev_rating/application/views/base.twig
+++ b/~dev_rating/application/views/base.twig
@@ -70,9 +70,12 @@
                                     {{ User.First }} {{ User.Last }}
 					</div>
 					| 
-                                {% if User.RoleMark == 4 %}
-                                {{ HTML.anchor('admin', 'Администрирование', {'title': 'Перейти в панель управления системой'})|raw }}
-                                {% endif %}
+					{% if (User.RoleMark b-and 16) != 0 %}
+						{{ HTML.anchor('dean_office', 'Деканат')|raw }}
+					{% endif %}
+					{% if User.RoleMark b-and 4 %}
+						{{ HTML.anchor('admin', 'Администрирование', {'title': 'Перейти в панель управления системой'})|raw }}
+					{% endif %}
 				{{ HTML.anchor('/', 'Главная страница', {'title': 'Перейти на главную страницу'})|raw }}
 				{{ HTML.anchor('settings', 'Настройки', {'title': 'Настроить аккаунт'})|raw }}
 				{{ HTML.anchor('sign/out', 'Выход', {'title': 'Выйти из системы'})|raw }}
@@ -124,5 +127,31 @@
 		</div>
 
 	</div>
+
+<div class="popup_overlay">
+    <div class="popup">
+	    <form action="sign" method='POST' id='signin_f'>
+            <div class="session_info">Пожалуйста, авторизируйтесь заново</div>
+            <div class='auth_error'>Неправильный логин/E-Mail или пароль</div>
+            <div class='inputs'>
+                <div class="auth_form">
+                    <input type="text" id="login" name="login" placeholder="Логин или E-Mail" value="">
+                </div>
+                <div class="auth_form">
+                    <input type="password" id="password" name="password" placeholder="Пароль" value="">
+                </div>
+            </div>
+            
+          <!--   {{ sign.input('signin_b', 'button', 'Войти') }} -->
+            <div class="auth_form">
+                <input type="button" id="signin_b" name="button" value="Войти">
+            </div>
+	    </form>
+	    <div class='actiongrid'>
+	        {# {{ HTML.anchor('remind', 'Забыли пароль?')|raw }} | #}
+	        {{ HTML.anchor('sign/up', 'Активировать аккаунт')|raw }} | {{ HTML.anchor('remind', 'Забыли пароль?')|raw }}
+	    </div>
+    </div>
+</div>	
 </body>
 </html>
\ No newline at end of file
diff --git a/~dev_rating/application/views/DeansOffice/index.twig b/~dev_rating/application/views/dean_office/index.twig
similarity index 93%
rename from ~dev_rating/application/views/DeansOffice/index.twig
rename to ~dev_rating/application/views/dean_office/index.twig
index c581837a3065a5ac7aa7ebb3e106d0707bfeaf4c..bcf6554493e3dc21e59091723260c463888116b6 100644
--- a/~dev_rating/application/views/DeansOffice/index.twig
+++ b/~dev_rating/application/views/dean_office/index.twig
@@ -5,7 +5,7 @@
     {{ HTML.script('media/js/jquery.fileDownload.js')|raw }}
 
     {{ HTML.style('media/css/discipline.css')|raw }}
-    {{ HTML.script('media/js/GetData.js')|raw }}
+    {{ HTML.script('media/js/getFinalForms.js')|raw }}
 {% endblock %}
 
 {% block main_top_title %}Деканат > Ведомости{% endblock %}
@@ -18,10 +18,10 @@
                 <div class="title">Форма контроля:</div>
                 <div class="field">
                     <div class="ExamTypeDiv">
-                        <input id="ExamType" name="ExamType" type="radio" value="exam"> Экзамен
+                        <input id="ExamType" name="ExamType" type="radio" value="exam" disabled> Экзамен
                     </div>
                     <div class="ExamTypeDiv">
-                        <input id="ExamType" name="ExamType" type="radio" value="credit"> Зачет
+                        <input id="ExamType" name="ExamType" type="radio" value="credit" checked> Зачет
                     </div>
                 </div>
             </div>
diff --git a/~dev_rating/application/views/student/index.twig b/~dev_rating/application/views/student/index.twig
index c06909673f8cc40ee1778de9e1e59aa1d608a274..90d0fc25fc5d2b28cd23c7a0de670d9467963005 100644
--- a/~dev_rating/application/views/student/index.twig
+++ b/~dev_rating/application/views/student/index.twig
@@ -1,42 +1,42 @@
 {% extends 'base' %} 
  
 {% macro subject(i, HTML) %}
-	<tr class="course_content">
-                <td class="{{ i.ColorScheme }}">
-                    <div class='Circle'></div>
-                </td>
-		<td class = "course_name">
-                    {{ HTML.anchor('subject/' ~ i.ID, i.Title)|raw }}
-		</td>
+<tr class="course_content">
+    <td class="{{ i.ColorScheme }}">
+        <div class='Circle'></div>
+    </td>
+    <td class = "course_name">
+        {{ HTML.anchor('subject/' ~ i.ID, i.Title)|raw }}
+    </td>
 
-		<td class="course_teacher">
-                    {% for teacher in i.Teachers %}
-                        <div>{{ teacher }}</div>
-                    {% else %}
-                        ---
-                    {% endfor %}
-		</td>
+    <td class="course_teacher">
+        {% for teacher in i.Teachers %}
+            <div>{{ teacher }}</div>
+        {% else %}
+            ---
+        {% endfor %}
+    </td>
 
-		<td  class="course_form_control">
-                    {{ i.Control }}
-		</td>	
+    <td  class="course_form_control">
+        {{ i.Control }}
+    </td>
 
-		<td class="course_rating_value">
-                    {{ i.Rate|default('0') }} / {{ i.MaxCurrentRate|default('0') }} / {{ i.MaxRate }}
-		</td>
+    <td class="course_rating_value">
+        {{ i.Rate|default('0') }} / {{ i.MaxCurrentRate|default('0') }} / {{ i.MaxRate }}
+    </td>
 
-		<td class="course_rating_percent">
-                    {% if i.MaxCurrentRate != 0 %}
-						{% if i.Rate > i.MaxCurrentRate %}
-							100%
-						{% else %}
-							{{ (100 * i.Rate) // i.MaxCurrentRate }} %
-						{% endif %}
-                    {% else %}
-                        ---
-                    {% endif %}
-		</td>
-	</tr>	
+    <td class="course_rating_percent">
+        {% if i.MaxCurrentRate != 0 %}
+            {% if i.Rate > i.MaxCurrentRate %}
+                100%
+            {% else %}
+                {{ (100 * i.Rate) // i.MaxCurrentRate }} %
+            {% endif %}
+        {% else %}
+            ---
+        {% endif %}
+    </td>
+</tr>
 {% endmacro %}
 {% import 'student/index' as res %}
 
diff --git a/~dev_rating/application/views/teacher/discipline/CreateDiscipline.twig b/~dev_rating/application/views/teacher/discipline/CreateDiscipline.twig
index 6cffa5191f60f94cd1f7e71486a0bc4eea4e9897..544f53a17d6a5ccbdd27d9b1ed069d057c0c77c1 100644
--- a/~dev_rating/application/views/teacher/discipline/CreateDiscipline.twig
+++ b/~dev_rating/application/views/teacher/discipline/CreateDiscipline.twig
@@ -7,6 +7,9 @@
 	{{ HTML.script('media/js/discipline/general.js')|raw }}
 	{{ HTML.script('media/js/discipline/CreateDiscipline.js')|raw }}
 	{{ HTML.style('media/css/discipline.css')|raw }}
+
+	{{ HTML.style('media/css/select2.css')|raw }}
+	{{ HTML.script('media/js/select2.js')|raw }}
 {% endblock %}
 
 {% block main_top_title %}Создание дисциплины{% endblock %}
@@ -49,16 +52,15 @@
 		<div class="itemBlock">
 			<div class="title">Предмет:</div>
 			<div class="field">
-				<input class="InputSubject default_input_text" placeholder="Начните вводить название предмета" value="">
-				<select size="4" class="SelectSubject default_select">
-					<option>-Не выбран-</option>
+				<select class="SelectSubject">
+                                        <option></option>
 					{% for Subject in SubjectsList %}
 						<option value="{{ Subject.ID }}" {% if Discipline.SubjectID == Subject.ID %}selected{% endif %}>{{ Subject.Title }}</option>
 					{% endfor %}
 				</select>
 			</div>
 		</div>
-		<p class="help">Если вашего предмета нет в списке - пожалуйста, напишите на нам. Кнопка 'Сообщение'</p>
+		<p class="help">Если Вы не нашли предмет в данном списке, пожалуйста, обратитесь в поддержку с помощью кнопки "Сообщение".</p>
 	</div>
 	<div class="LayerSection">
 		<div class="itemBlock">
diff --git a/~dev_rating/application/views/teacher/discipline/EditSettings.twig b/~dev_rating/application/views/teacher/discipline/EditSettings.twig
index 6fb81cb7429a5e87318327c729972b36cb43c054..e6d821136834711c93b817b14bb6cbd21e7c1b60 100644
--- a/~dev_rating/application/views/teacher/discipline/EditSettings.twig
+++ b/~dev_rating/application/views/teacher/discipline/EditSettings.twig
@@ -4,6 +4,8 @@
 
 {% block discipline_media %}
 	{{ HTML.script('media/js/discipline/EditSettings.js')|raw }}
+        {{ HTML.style('media/css/select2.css')|raw }}
+	{{ HTML.script('media/js/select2.js')|raw }}
 {% endblock %}
 
 {% block map_content %}
@@ -36,15 +38,15 @@
 		<div class="itemBlock">
 			<div class="title">Предмет:</div>
 			<div class="field">
-				<input class="InputSubject default_input_text {% if Discipline.isLocked == 1 %}disabled_select{%endif%}" placeholder="Начните вводить название предмет" value="{{ Discipline.SubjectName }}" {% if Discipline.isLocked == 1 %}disabled{%endif%}>
-				<select size="4" class="SelectSubject default_select">
+				<select class="SelectSubject">
+                                        <option></option>
 					{% for Subject in SubjectsList %}
 						<option value="{{ Subject.ID }}" {% if Discipline.SubjectID == Subject.ID %}selected{% endif %}>{{ Subject.Title }}</option>
 					{% endfor %}
 				</select>
 			</div>
 		</div>
-		<p class="help">Если вашего предмета нет в списке - пожалуйста, напишите на нам. Кнопка 'Сообщение'</p>
+		<p class="help">Если Вы не нашли предмет в данном списке, пожалуйста, обратитесь в поддержку с помощью кнопки "Сообщение".</p>
 	</div>
 	<div class="LayerSection">
 		<div class="itemBlock">
diff --git a/~dev_rating/application/views/teacher/discipline/MapBase.twig b/~dev_rating/application/views/teacher/discipline/MapBase.twig
index 56edf30b152e9e70d67465be11a7442011139078..aad3ae5c5c98c982fc9eb684c9d9e334fc068053 100644
--- a/~dev_rating/application/views/teacher/discipline/MapBase.twig
+++ b/~dev_rating/application/views/teacher/discipline/MapBase.twig
@@ -14,7 +14,9 @@
 {% block main_top_title %}Редактирование дисциплины{% endblock %}
 {% block main_content %}
 	{% if Discipline.isLocked == 1 %}
-		<p class="canNotEdit" >Был добавлен первый балл. Редактировать <u>базовые настройки</u>, <u>модули</u> и <u>группы</u> нельзя.</p>
+		<p class="canNotEdit">
+			Был добавлен первый балл. Редактирование <u>базовых настроек</u>, <u>модулей</u> и <u>групп</u> невозможно.
+		</p>
 	{% endif %}
 	<div class="tabsWrapper">
 		<div class="tabs">
diff --git a/~dev_rating/application/views/teacher/exam.twig b/~dev_rating/application/views/teacher/exam.twig
index ab9d75ee220b0ef148c1b66ff4d5b760c67f553f..096ffba4714dcd19ad69fb5f62fe117d6eb663fb 100644
--- a/~dev_rating/application/views/teacher/exam.twig
+++ b/~dev_rating/application/views/teacher/exam.twig
@@ -81,7 +81,7 @@
 					<span class="downloadExcelStatement" id="group_{{ group.GroupID }}">Скачать ведомость</span>
 				</td>
 			</tr>
-		{#
+
 			{% for student in group.Students %}
 				{% set row = row + 1 %}
 				{% set col = 0 %}
@@ -114,7 +114,6 @@
 					<td class="rateResultCell staticCell">{{ student.RateResult }}</td>
 				</tr>
 			{% endfor %} 
-		#}
 
 
 		{% endfor %}
@@ -127,6 +126,6 @@
 		</div>
 	</div>
 	<div id="hidden_div">
-		{ "studyGroupID": "{{ studyGroupID }}" }
+		{{ disciplineInfo_JSON|raw }}
 	</div>
 {% endblock %}
\ No newline at end of file
diff --git a/~dev_rating/application/views/teacher/rating.twig b/~dev_rating/application/views/teacher/rating.twig
index 6c63dfc15c823200904ed248c52f6954679f892c..d2298cd4f8a06f3a0c055b1a97e03d76295abb14 100644
--- a/~dev_rating/application/views/teacher/rating.twig
+++ b/~dev_rating/application/views/teacher/rating.twig
@@ -15,7 +15,8 @@
 	<h2 class="h2_titleSubject">{{ SubjectName }}</h2>
 
 	<button class="downloadExcel" style="display: none">Скачать в excel формате [dev version]</button>
-    {{ HTML.anchor('exam/'~DisciplineID, 
+
+    {{ HTML.anchor('exam/'~disciplineInfo.ID, 
             "Перейти к сессии →", 
             {'title': 'Сессия', 'class': 'exam_a'})|raw }}
 
@@ -97,7 +98,7 @@
 							</td>
 						{% else %}
 							{% set j = j - 1 %}
-							<td class="staticCell">
+							<td class="staticCell{% if student.Rates[i].Type == 'exam' %} examCell{% endif %}">
 								<p>{{ student.Rates[i].Rate }}</p>
 							</td>
 						{% endif %}
@@ -116,6 +117,6 @@
 		</div>
 	</div>
 	<div id="hidden_div">
-		{ "studyGroupID": "{{ studyGroupID }}" }
+		{{ disciplineInfo_JSON|raw }}
 	</div>
 {% endblock %}
\ No newline at end of file
diff --git a/~dev_rating/deploy.php b/~dev_rating/deploy.php
index bd691ffee9c65058a9da69c1654824366b4d1666..71cb15a88a1e531058911b80556604e83057ef77 100644
--- a/~dev_rating/deploy.php
+++ b/~dev_rating/deploy.php
@@ -9,6 +9,8 @@
                 echo 'Беда!';
         if(!copy(CFGPATH.'twig.php', APPPATH.'config/twig.php'))
                 echo 'Беда!';   
+        if(!copy(CFGPATH.'session.php', APPPATH.'config/session.php'))
+                echo 'Беда!';  
     }
     
     if(!is_dir(APPPATH.'logs/'))
diff --git a/~dev_rating/deployConfig/session.php b/~dev_rating/deployConfig/session.php
new file mode 100644
index 0000000000000000000000000000000000000000..8fc0f93187eb2db5174113d13fb1b6b723424a4c
--- /dev/null
+++ b/~dev_rating/deployConfig/session.php
@@ -0,0 +1,8 @@
+<?php
+
+return array(
+    'native' => array(
+        'name' => 'session_name',
+        'lifetime' => 1800,
+    )
+);
\ No newline at end of file
diff --git a/~dev_rating/docs/template credit.xls b/~dev_rating/docs/template credit.xls
index c94873a0a043c1f27038116883bc280c31c100c6..3efe9c7f2eecc7d658d857fb90e482818417b15a 100644
Binary files a/~dev_rating/docs/template credit.xls and b/~dev_rating/docs/template credit.xls differ
diff --git a/~dev_rating/docs/template exam.xls b/~dev_rating/docs/template exam.xls
index b272e99f3b750f5b97c13fe99a67e7af4eb5f5eb..682553295565fb2e1931608da9f2bebac94b6c46 100644
Binary files a/~dev_rating/docs/template exam.xls and b/~dev_rating/docs/template exam.xls differ
diff --git a/~dev_rating/media/css/base.css b/~dev_rating/media/css/base.css
index 8133da5928333fa1691e1300a38e269926d31fb6..a4d6eddabae6cc6fabaa34d8d03b47ff2d90d1ce 100644
--- a/~dev_rating/media/css/base.css
+++ b/~dev_rating/media/css/base.css
@@ -299,8 +299,130 @@ h2.BlueTitle{
 
 
 
+/*----------------  модальное окно  -------------------*/
+.session_info {
+    font: 9pt;
+    color: #aaaaaa;
+}
+
+.auth_form
+{
+    padding: 3px;
+    margin: 0 auto;
+    text-align: center;
+}
+
+.auth_form input
+{
+    margin: 0 auto;
+    margin: 1px 0;
+    border: 1px solid;
+    border-radius: 3px;
+}
+
+.auth_form input[type=text], .auth_form input[type=password]
+{
+    font-size: 9pt;
+    width: 90%;
+    border-color: #aaaaaa;
+    overflow: hidden;
+    padding: 7px; 
+}
+
+.auth_form input[type=button], .auth_form input[type=submit]
+{
+    margin: 0 auto;
+    width: 95%;
+    overflow: hidden;
+    padding: 7px;
+    background-color: #009933;
+    border: none;
+    color: #ffffff;
+}
+
+.auth_form input[type=button]:hover, .auth_form input[type=submit]:hover
+{
+    cursor: pointer;
+    background-color: #009000;
+}
+
 
+.popup_overlay {
+	z-index: 9999;
+    display: none;
+    position: fixed;
+    left: 0;
+    top: 0;
+    width: 100%;
+    height: 100%;
+    background: rgba(0,0,0,.7);
+    text-align: center;
+
+
+	
+}
+
+.popup_overlay:after {
+    display: inline-block;
+    height: 100%;
+    width: 0;
+    vertical-align: middle;
+    content: '';
+}
+
+.popup {
+    display: inline-block;
+    position: relative;
+    max-width: 300px;
+    padding: 20px;
+    border: 3px solid #fff;
+    border-radius: 10px;
+    box-shadow: inset 0 1px 2px 1px rgba(0,0,0,.4);
+    background: #fff;
+    vertical-align: middle
+}
+
+
+.auth_error {
+    display: none;
+    font-size: 9pt;
+    color: red;
+}
+
+.inputs {
+    margin: 0 auto;
+    margin-top: 5px;
+}
+
+.actiongrid {
+	margin-top: 5px;
+}
+
+.popup_overlay:target > div {
+-webkit-animation-name: bounce;
+}
+
+
+@-webkit-keyframes bounce {
+  0% {
+  	-webkit-transform: scale3d(0.1,0.1,1);
+  	-webkit-box-shadow: 0 3px 20px rgba(0,0,0,0.9);
+  }
+  55% {
+  	-webkit-transform: scale3d(1.08,1.08,1);
+  	-webkit-box-shadow: 0 10px 20px rgba(0,0,0,0);
+  }
+  75% {
+  	-webkit-transform: scale3d(0.95,0.95,1);
+  	-webkit-box-shadow: 0 0 20px rgba(0,0,0,0.9);
+  }
+  100% {
+  	-webkit-transform: scale3d(1,1,1);
+  	-webkit-box-shadow: 0 3px 20px rgba(0,0,0,0.9);
+  }
+}
 
 
 
 
+/*----------------------------------------------------*/
diff --git a/~dev_rating/media/css/discipline.css b/~dev_rating/media/css/discipline.css
index c844a748791eab1e671edb08fcb3b5ed62e6da3f..6e900b92cc58b41b47b11f6942770c89d0f1b1b5 100644
--- a/~dev_rating/media/css/discipline.css
+++ b/~dev_rating/media/css/discipline.css
@@ -112,9 +112,9 @@
 		width: 100%;
 	}
 	.LayerSection .SelectSubject {
-		display: none;
+		/*display: none;*/
 		width: 300px;
-		position: absolute;
+		/*position: absolute;*/
 		/*
 		position: absolute;
 		top: 15px;
diff --git a/~dev_rating/media/css/icons/select2-spinner.gif b/~dev_rating/media/css/icons/select2-spinner.gif
new file mode 100644
index 0000000000000000000000000000000000000000..5b33f7e54f4e55b6b8774d86d96895db9af044b4
Binary files /dev/null and b/~dev_rating/media/css/icons/select2-spinner.gif differ
diff --git a/~dev_rating/media/css/icons/select2.png b/~dev_rating/media/css/icons/select2.png
new file mode 100644
index 0000000000000000000000000000000000000000..1d804ffb99699b9e030f1010314de0970b5a000d
Binary files /dev/null and b/~dev_rating/media/css/icons/select2.png differ
diff --git a/~dev_rating/media/css/icons/select2x2.png b/~dev_rating/media/css/icons/select2x2.png
new file mode 100644
index 0000000000000000000000000000000000000000..4bdd5c961d452c49dfa0789c2c7ffb82c238fc24
Binary files /dev/null and b/~dev_rating/media/css/icons/select2x2.png differ
diff --git a/~dev_rating/media/css/select2.css b/~dev_rating/media/css/select2.css
new file mode 100644
index 0000000000000000000000000000000000000000..7677a9c71b9a0ea2c992350163da901ff2433a8b
--- /dev/null
+++ b/~dev_rating/media/css/select2.css
@@ -0,0 +1,702 @@
+/*
+Version: 3.5.2 Timestamp: Sat Nov  1 14:43:36 EDT 2014
+*/
+.select2-container {
+    margin: 0;
+    position: relative;
+    display: inline-block;
+    /* inline-block for ie7 */
+    zoom: 1;
+    *display: inline;
+    vertical-align: middle;
+}
+
+.select2-container,
+.select2-drop,
+.select2-search,
+.select2-search input {
+  /*
+    Force border-box so that % widths fit the parent
+    container without overlap because of margin/padding.
+    More Info : http://www.quirksmode.org/css/box.html
+  */
+  -webkit-box-sizing: border-box; /* webkit */
+     -moz-box-sizing: border-box; /* firefox */
+          box-sizing: border-box; /* css3 */
+}
+
+.select2-container .select2-choice {
+    display: block;
+    height: 26px;
+    padding: 0 0 0 8px;
+    overflow: hidden;
+    position: relative;
+
+    border: 1px solid #aaa;
+    white-space: nowrap;
+    line-height: 26px;
+    color: #444;
+    text-decoration: none;
+
+
+
+    background-clip: padding-box;
+
+    -webkit-touch-callout: none;
+      -webkit-user-select: none;
+         -moz-user-select: none;
+          -ms-user-select: none;
+              user-select: none;
+
+    background-color: #fff;
+    background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eee), color-stop(0.5, #fff));
+    background-image: -webkit-linear-gradient(center bottom, #eee 0%, #fff 50%);
+    background-image: -moz-linear-gradient(center bottom, #eee 0%, #fff 50%);
+    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#ffffff', endColorstr = '#eeeeee', GradientType = 0);
+    background-image: linear-gradient(to top, #eee 0%, #fff 50%);
+}
+
+html[dir="rtl"] .select2-container .select2-choice {
+    padding: 0 8px 0 0;
+}
+
+.select2-container.select2-drop-above .select2-choice {
+    border-bottom-color: #aaa;
+
+    border-radius: 0 0 4px 4px;
+
+    background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eee), color-stop(0.9, #fff));
+    background-image: -webkit-linear-gradient(center bottom, #eee 0%, #fff 90%);
+    background-image: -moz-linear-gradient(center bottom, #eee 0%, #fff 90%);
+    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#eeeeee', GradientType=0);
+    background-image: linear-gradient(to bottom, #eee 0%, #fff 90%);
+}
+
+.select2-container.select2-allowclear .select2-choice .select2-chosen {
+    margin-right: 42px;
+}
+
+.select2-container .select2-choice > .select2-chosen {
+    margin-right: 26px;
+    display: block;
+    overflow: hidden;
+
+    white-space: nowrap;
+
+    text-overflow: ellipsis;
+    float: none;
+    width: auto;
+}
+
+html[dir="rtl"] .select2-container .select2-choice > .select2-chosen {
+    margin-left: 26px;
+    margin-right: 0;
+}
+
+.select2-container .select2-choice abbr {
+    display: none;
+    width: 12px;
+    height: 12px;
+    position: absolute;
+    right: 24px;
+    top: 8px;
+
+    font-size: 14px;
+    text-decoration: none;
+
+    border: 0;
+    background: url('icons/select2.png') right top no-repeat;
+    cursor: pointer;
+    outline: 0;
+}
+
+.select2-container.select2-allowclear .select2-choice abbr {
+    display: inline-block;
+}
+
+.select2-container .select2-choice abbr:hover {
+    background-position: right -11px;
+    cursor: pointer;
+}
+
+.select2-drop-mask {
+    border: 0;
+    margin: 0;
+    padding: 0;
+    position: fixed;
+    left: 0;
+    top: 0;
+    min-height: 100%;
+    min-width: 100%;
+    height: auto;
+    width: auto;
+    opacity: 0;
+    z-index: 9998;
+    /* styles required for IE to work */
+    background-color: #fff;
+    filter: alpha(opacity=0);
+}
+
+.select2-drop {
+    width: 100%;
+    margin-top: -1px;
+    position: absolute;
+    z-index: 9999;
+    top: 100%;
+
+    background: #fff;
+    color: #000;
+    border: 1px solid #aaa;
+    border-top: 0;
+
+    border-radius: 0 0 4px 4px;
+
+    -webkit-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
+            box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
+}
+
+.select2-drop.select2-drop-above {
+    margin-top: 1px;
+    border-top: 1px solid #aaa;
+    border-bottom: 0;
+
+    border-radius: 4px 4px 0 0;
+
+    -webkit-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
+            box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
+}
+
+.select2-drop-active {
+    border: 1px solid #5897fb;
+    border-top: none;
+}
+
+.select2-drop.select2-drop-above.select2-drop-active {
+    border-top: 1px solid #5897fb;
+}
+
+.select2-drop-auto-width {
+    border-top: 1px solid #aaa;
+    width: auto;
+}
+
+.select2-drop-auto-width .select2-search {
+    padding-top: 4px;
+}
+
+.select2-container .select2-choice .select2-arrow {
+    display: inline-block;
+    width: 18px;
+    height: 100%;
+    position: absolute;
+    right: 0;
+    top: 0;
+
+    border-left: 1px solid #aaa;
+    border-radius: 0 4px 4px 0;
+
+    background-clip: padding-box;
+
+    background: #ccc;
+    background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #ccc), color-stop(0.6, #eee));
+    background-image: -webkit-linear-gradient(center bottom, #ccc 0%, #eee 60%);
+    background-image: -moz-linear-gradient(center bottom, #ccc 0%, #eee 60%);
+    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#eeeeee', endColorstr = '#cccccc', GradientType = 0);
+    background-image: linear-gradient(to top, #ccc 0%, #eee 60%);
+}
+
+html[dir="rtl"] .select2-container .select2-choice .select2-arrow {
+    left: 0;
+    right: auto;
+
+    border-left: none;
+    border-right: 1px solid #aaa;
+    border-radius: 4px 0 0 4px;
+}
+
+.select2-container .select2-choice .select2-arrow b {
+    display: block;
+    width: 100%;
+    height: 100%;
+    background: url('icons/select2.png') no-repeat 0 1px;
+}
+
+html[dir="rtl"] .select2-container .select2-choice .select2-arrow b {
+    background-position: 2px 1px;
+}
+
+.select2-search {
+    display: inline-block;
+    width: 100%;
+    min-height: 26px;
+    margin: 0;
+    padding-left: 4px;
+    padding-right: 4px;
+
+    position: relative;
+    z-index: 10000;
+
+    white-space: nowrap;
+}
+
+.select2-search input {
+    width: 100%;
+    height: auto !important;
+    min-height: 26px;
+    padding: 4px 20px 4px 5px;
+    margin: 0;
+
+    outline: 0;
+    font-size: 14px;
+
+    border: 1px solid #aaa;
+    border-radius: 0;
+
+    -webkit-box-shadow: none;
+            box-shadow: none;
+
+    background: #fff url('icons/select2.png') no-repeat 100% -22px;
+    background: url('icons/select2.png') no-repeat 100% -22px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, #fff), color-stop(0.99, #eee));
+    background: url('icons/select2.png') no-repeat 100% -22px, -webkit-linear-gradient(center bottom, #fff 85%, #eee 99%);
+    background: url('icons/select2.png') no-repeat 100% -22px, -moz-linear-gradient(center bottom, #fff 85%, #eee 99%);
+    background: url('icons/select2.png') no-repeat 100% -22px, linear-gradient(to bottom, #fff 85%, #eee 99%) 0 0;
+}
+
+html[dir="rtl"] .select2-search input {
+    padding: 4px 5px 4px 20px;
+
+    background: #fff url('icons/select2.png') no-repeat -37px -22px;
+    background: url('icons/select2.png') no-repeat -37px -22px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, #fff), color-stop(0.99, #eee));
+    background: url('icons/select2.png') no-repeat -37px -22px, -webkit-linear-gradient(center bottom, #fff 85%, #eee 99%);
+    background: url('icons/select2.png') no-repeat -37px -22px, -moz-linear-gradient(center bottom, #fff 85%, #eee 99%);
+    background: url('icons/select2.png') no-repeat -37px -22px, linear-gradient(to bottom, #fff 85%, #eee 99%) 0 0;
+}
+
+.select2-drop.select2-drop-above .select2-search input {
+    margin-top: 4px;
+}
+
+.select2-search input.select2-active {
+    background: #fff url('icons/select2-spinner.gif') no-repeat 100%;
+    background: url('icons/select2-spinner.gif') no-repeat 100%, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, #fff), color-stop(0.99, #eee));
+    background: url('icons/select2-spinner.gif') no-repeat 100%, -webkit-linear-gradient(center bottom, #fff 85%, #eee 99%);
+    background: url('icons/select2-spinner.gif') no-repeat 100%, -moz-linear-gradient(center bottom, #fff 85%, #eee 99%);
+    background: url('icons/select2-spinner.gif') no-repeat 100%, linear-gradient(to bottom, #fff 85%, #eee 99%) 0 0;
+}
+
+.select2-container-active .select2-choice,
+.select2-container-active .select2-choices {
+    border: 1px solid #5897fb;
+    outline: none;
+
+    -webkit-box-shadow: 0 0 5px rgba(0, 0, 0, .3);
+            box-shadow: 0 0 5px rgba(0, 0, 0, .3);
+}
+
+.select2-dropdown-open .select2-choice {
+    border-bottom-color: transparent;
+    -webkit-box-shadow: 0 1px 0 #fff inset;
+            box-shadow: 0 1px 0 #fff inset;
+
+    border-bottom-left-radius: 0;
+    border-bottom-right-radius: 0;
+
+    background-color: #eee;
+    background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #fff), color-stop(0.5, #eee));
+    background-image: -webkit-linear-gradient(center bottom, #fff 0%, #eee 50%);
+    background-image: -moz-linear-gradient(center bottom, #fff 0%, #eee 50%);
+    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0);
+    background-image: linear-gradient(to top, #fff 0%, #eee 50%);
+}
+
+.select2-dropdown-open.select2-drop-above .select2-choice,
+.select2-dropdown-open.select2-drop-above .select2-choices {
+    border: 1px solid #5897fb;
+    border-top-color: transparent;
+
+    background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #fff), color-stop(0.5, #eee));
+    background-image: -webkit-linear-gradient(center top, #fff 0%, #eee 50%);
+    background-image: -moz-linear-gradient(center top, #fff 0%, #eee 50%);
+    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0);
+    background-image: linear-gradient(to bottom, #fff 0%, #eee 50%);
+}
+
+.select2-dropdown-open .select2-choice .select2-arrow {
+    background: transparent;
+    border-left: none;
+    filter: none;
+}
+html[dir="rtl"] .select2-dropdown-open .select2-choice .select2-arrow {
+    border-right: none;
+}
+
+.select2-dropdown-open .select2-choice .select2-arrow b {
+    background-position: -18px 1px;
+}
+
+html[dir="rtl"] .select2-dropdown-open .select2-choice .select2-arrow b {
+    background-position: -16px 1px;
+}
+
+.select2-hidden-accessible {
+    border: 0;
+    clip: rect(0 0 0 0);
+    height: 1px;
+    margin: -1px;
+    overflow: hidden;
+    padding: 0;
+    position: absolute;
+    width: 1px;
+}
+
+/* results */
+.select2-results {
+    max-height: 200px;
+    padding: 0 0 0 4px;
+    margin: 4px 4px 4px 0;
+    position: relative;
+    overflow-x: hidden;
+    overflow-y: auto;
+    -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+}
+
+html[dir="rtl"] .select2-results {
+    padding: 0 4px 0 0;
+    margin: 4px 0 4px 4px;
+}
+
+.select2-results ul.select2-result-sub {
+    margin: 0;
+    padding-left: 0;
+}
+
+.select2-results li {
+    list-style: none;
+    display: list-item;
+    background-image: none;
+}
+
+.select2-results li.select2-result-with-children > .select2-result-label {
+    font-weight: bold;
+}
+
+.select2-results .select2-result-label {
+    padding: 3px 7px 4px;
+    margin: 0;
+    cursor: pointer;
+
+    min-height: 1em;
+
+    -webkit-touch-callout: none;
+      -webkit-user-select: none;
+         -moz-user-select: none;
+          -ms-user-select: none;
+              user-select: none;
+}
+
+.select2-results-dept-1 .select2-result-label { padding-left: 20px }
+.select2-results-dept-2 .select2-result-label { padding-left: 40px }
+.select2-results-dept-3 .select2-result-label { padding-left: 60px }
+.select2-results-dept-4 .select2-result-label { padding-left: 80px }
+.select2-results-dept-5 .select2-result-label { padding-left: 100px }
+.select2-results-dept-6 .select2-result-label { padding-left: 110px }
+.select2-results-dept-7 .select2-result-label { padding-left: 120px }
+
+.select2-results .select2-highlighted {
+    background: #3875d7;
+    color: #fff;
+}
+
+.select2-results li em {
+    background: #feffde;
+    font-style: normal;
+}
+
+.select2-results .select2-highlighted em {
+    background: transparent;
+}
+
+.select2-results .select2-highlighted ul {
+    background: #fff;
+    color: #000;
+}
+
+.select2-results .select2-no-results,
+.select2-results .select2-searching,
+.select2-results .select2-ajax-error,
+.select2-results .select2-selection-limit {
+    background: #f4f4f4;
+    display: list-item;
+    padding-left: 5px;
+}
+
+/*
+disabled look for disabled choices in the results dropdown
+*/
+.select2-results .select2-disabled.select2-highlighted {
+    color: #666;
+    background: #f4f4f4;
+    display: list-item;
+    cursor: default;
+}
+.select2-results .select2-disabled {
+  background: #f4f4f4;
+  display: list-item;
+  cursor: default;
+}
+
+.select2-results .select2-selected {
+    display: none;
+}
+
+.select2-more-results.select2-active {
+    background: #f4f4f4 url('icons/select2-spinner.gif') no-repeat 100%;
+}
+
+.select2-results .select2-ajax-error {
+    background: rgba(255, 50, 50, .2);
+}
+
+.select2-more-results {
+    background: #f4f4f4;
+    display: list-item;
+}
+
+/* disabled styles */
+
+.select2-container.select2-container-disabled .select2-choice {
+    background-color: #f4f4f4;
+    background-image: none;
+    border: 1px solid #ddd;
+    cursor: default;
+}
+
+.select2-container.select2-container-disabled .select2-choice .select2-arrow {
+    background-color: #f4f4f4;
+    background-image: none;
+    border-left: 0;
+}
+
+.select2-container.select2-container-disabled .select2-choice abbr {
+    display: none;
+}
+
+
+/* multiselect */
+
+.select2-container-multi .select2-choices {
+    height: auto !important;
+    height: 1%;
+    margin: 0;
+    padding: 0 5px 0 0;
+    position: relative;
+
+    border: 1px solid #aaa;
+    cursor: text;
+    overflow: hidden;
+
+    background-color: #fff;
+    background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(1%, #eee), color-stop(15%, #fff));
+    background-image: -webkit-linear-gradient(top, #eee 1%, #fff 15%);
+    background-image: -moz-linear-gradient(top, #eee 1%, #fff 15%);
+    background-image: linear-gradient(to bottom, #eee 1%, #fff 15%);
+}
+
+html[dir="rtl"] .select2-container-multi .select2-choices {
+    padding: 0 0 0 5px;
+}
+
+.select2-locked {
+  padding: 3px 5px 3px 5px !important;
+}
+
+.select2-container-multi .select2-choices {
+    min-height: 26px;
+}
+
+.select2-container-multi.select2-container-active .select2-choices {
+    border: 1px solid #5897fb;
+    outline: none;
+
+    -webkit-box-shadow: 0 0 5px rgba(0, 0, 0, .3);
+            box-shadow: 0 0 5px rgba(0, 0, 0, .3);
+}
+.select2-container-multi .select2-choices li {
+    float: left;
+    list-style: none;
+}
+html[dir="rtl"] .select2-container-multi .select2-choices li
+{
+    float: right;
+}
+.select2-container-multi .select2-choices .select2-search-field {
+    margin: 0;
+    padding: 0;
+    white-space: nowrap;
+}
+
+.select2-container-multi .select2-choices .select2-search-field input {
+    padding: 5px;
+    margin: 1px 0;
+
+    font-size: 100%;
+    color: #666;
+    outline: 0;
+    border: 0;
+    -webkit-box-shadow: none;
+            box-shadow: none;
+    background: transparent !important;
+}
+
+.select2-container-multi .select2-choices .select2-search-field input.select2-active {
+    background: #fff url('icons/select2-spinner.gif') no-repeat 100% !important;
+}
+
+.select2-default {
+    color: #999 !important;
+}
+
+.select2-container-multi .select2-choices .select2-search-choice {
+    padding: 3px 5px 3px 18px;
+    margin: 3px 0 3px 5px;
+    position: relative;
+
+    line-height: 13px;
+    color: #333;
+    cursor: default;
+    border: 1px solid #aaaaaa;
+
+    border-radius: 3px;
+
+    -webkit-box-shadow: 0 0 2px #fff inset, 0 1px 0 rgba(0, 0, 0, 0.05);
+            box-shadow: 0 0 2px #fff inset, 0 1px 0 rgba(0, 0, 0, 0.05);
+
+    background-clip: padding-box;
+
+    -webkit-touch-callout: none;
+      -webkit-user-select: none;
+         -moz-user-select: none;
+          -ms-user-select: none;
+              user-select: none;
+
+    background-color: #e4e4e4;
+    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#f4f4f4', GradientType=0);
+    background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eee));
+    background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eee 100%);
+    background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eee 100%);
+    background-image: linear-gradient(to bottom, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eee 100%);
+}
+html[dir="rtl"] .select2-container-multi .select2-choices .select2-search-choice
+{
+    margin: 3px 5px 3px 0;
+    padding: 3px 18px 3px 5px;
+}
+.select2-container-multi .select2-choices .select2-search-choice .select2-chosen {
+    cursor: default;
+}
+.select2-container-multi .select2-choices .select2-search-choice-focus {
+    background: #d4d4d4;
+}
+
+.select2-search-choice-close {
+    display: block;
+    width: 12px;
+    height: 13px;
+    position: absolute;
+    right: 3px;
+    top: 4px;
+
+    font-size: 14px;
+    outline: none;
+    background: url('icons/select2.png') right top no-repeat;
+}
+html[dir="rtl"] .select2-search-choice-close {
+    right: auto;
+    left: 3px;
+}
+
+.select2-container-multi .select2-search-choice-close {
+    left: 3px;
+}
+
+html[dir="rtl"] .select2-container-multi .select2-search-choice-close {
+    left: auto;
+    right: 2px;
+}
+
+.select2-container-multi .select2-choices .select2-search-choice .select2-search-choice-close:hover {
+  background-position: right -11px;
+}
+.select2-container-multi .select2-choices .select2-search-choice-focus .select2-search-choice-close {
+    background-position: right -11px;
+}
+
+/* disabled styles */
+.select2-container-multi.select2-container-disabled .select2-choices {
+    background-color: #f4f4f4;
+    background-image: none;
+    border: 1px solid #ddd;
+    cursor: default;
+}
+
+.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice {
+    padding: 3px 5px 3px 5px;
+    border: 1px solid #ddd;
+    background-image: none;
+    background-color: #f4f4f4;
+}
+
+.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice .select2-search-choice-close {    display: none;
+    background: none;
+}
+/* end multiselect */
+
+
+.select2-result-selectable .select2-match,
+.select2-result-unselectable .select2-match {
+    text-decoration: underline;
+}
+
+.select2-offscreen, .select2-offscreen:focus {
+    clip: rect(0 0 0 0) !important;
+    width: 1px !important;
+    height: 1px !important;
+    border: 0 !important;
+    margin: 0 !important;
+    padding: 0 !important;
+    overflow: hidden !important;
+    position: absolute !important;
+    outline: 0 !important;
+    left: 0px !important;
+    top: 0px !important;
+}
+
+.select2-display-none {
+    display: none;
+}
+
+.select2-measure-scrollbar {
+    position: absolute;
+    top: -10000px;
+    left: -10000px;
+    width: 100px;
+    height: 100px;
+    overflow: scroll;
+}
+
+/* Retina-ize icons */
+
+@media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 2dppx)  {
+    .select2-search input,
+    .select2-search-choice-close,
+    .select2-container .select2-choice abbr,
+    .select2-container .select2-choice .select2-arrow b {
+        background-image: url('icons/select2x2.png') !important;
+        background-repeat: no-repeat !important;
+        background-size: 60px 40px !important;
+    }
+
+    .select2-search input {
+        background-position: 100% -21px !important;
+    }
+}
diff --git a/~dev_rating/media/css/student/index.css b/~dev_rating/media/css/student/index.css
index cb1ef170e363e43bca9e8788a8702a765a286f2a..100072dad840ee4e2167660aa8039654045535ce 100644
--- a/~dev_rating/media/css/student/index.css
+++ b/~dev_rating/media/css/student/index.css
@@ -96,27 +96,27 @@ tr.course_content td {
 }
 
 .ECTS-F > .Circle { 
-    background: #f60;
+    background: #8A2E15;
 }
 
 .ECTS-FX > .Circle { 
-    background: #e56800;
+    background: #D46141;
 }
 
 .ECTS-E > .Circle { 
-    background: #ffb100;
+    background: #EDEC51;
 }
 .ECTS-D > .Circle {
-    background: #ffd000;
+    background: #7EBA3C;
 }
 .ECTS-C > .Circle { 
-    background: #70c300;
+    background: #28AE57;
 }
 .ECTS-B > .Circle { 
-    background: #059e00;
+    background: #349920;
 }
 .ECTS-A > .Circle {
-    background: #009404;
+    background: #367F27;
 }
 
 .course_rating_percent > div 
diff --git a/~dev_rating/media/js/GetData.js b/~dev_rating/media/js/GetData.js
deleted file mode 100644
index b6f2a1dbac66f9b704f395e7115ad953bd6ee765..0000000000000000000000000000000000000000
--- a/~dev_rating/media/js/GetData.js
+++ /dev/null
@@ -1,52 +0,0 @@
-var $ = jQuery;
-$(function() {
-
-	//Получить список групп по ID курса
-    $("#SelectGrade").change(function() {
-        var gradeID = $(this).val(); 
-        if (gradeID >= 0)
-        {
-            $.post(
-                g_URLdir + "handler/GetData/GetGroups",
-                {   
-                    "GradeID": gradeID 
-                },
-                function(d){
-                    d = $.parseJSON(d);
-                    if(d.success === true) {
-                    	console.log(d.data);
-                    	var selectGroup = $("#SelectGroup");
-                    	var i = 0;
-                    	selectGroup.html("<option>-Не выбрана-</option>");
-                    	for (i in d.data)
-                    	{
-                    		group = d.data[i];
-                    		selectGroup.append("<option value='"+ group.ID +"'>Группа "+ group.GroupNum +" ("+ group.SpecAbbr +")</option>");
-                    		selectGroup.removeAttr("disabled");
-                    	}
-                    	$("#DownloadStatement").removeAttr("disabled");
-                    }
-                }
-            );
-        }
-    });
-
-    // Скачать ведомость
-     $('body').on('click', '#DownloadStatement', function(){
-        $.fileDownload( g_URLdir + 'handler/FileCreator/GenerateFullStatement', {
-            httpMethod: "POST",
-            data: 
-            {
-                "GroupID": parseInt($("#SelectGroup").val()),
-                "ExamType": $("#ExamType").val() 
-            },
-            successCallback: function () {
-
-            },
-            failCallback: function () {
-
-            }
-        });
-    });
-
-});
\ No newline at end of file
diff --git a/~dev_rating/media/js/discipline/DisciplineList.js b/~dev_rating/media/js/discipline/DisciplineList.js
index 52af5df2501d1687cf3f850d9997f4b9a357873c..8da800aa322c0e153b2ea743acd6fc282669afde 100644
--- a/~dev_rating/media/js/discipline/DisciplineList.js
+++ b/~dev_rating/media/js/discipline/DisciplineList.js
@@ -31,4 +31,4 @@ $(function() {
 
     });
 
-})
\ No newline at end of file
+});
diff --git a/~dev_rating/media/js/discipline/EditSettings.js b/~dev_rating/media/js/discipline/EditSettings.js
index 4e65039c1959e1ca27480ad523df7b88a42e1ae7..21ca5944e6a256c1093441e8678b2f0e9555681b 100644
--- a/~dev_rating/media/js/discipline/EditSettings.js
+++ b/~dev_rating/media/js/discipline/EditSettings.js
@@ -11,6 +11,7 @@ $(function() {
 
 
 // ---- Изменения базовых параметров дисциплины ---
+    $("select.SelectSubject").select2({allowClear: false});
     
     // Изменение предмета
     $("select.SelectSubject").change(function()
@@ -104,12 +105,13 @@ $(function() {
             },
             function(data) {
                 data = $.parseJSON(data);
-                if(data.success == 0) {
+                var status = parseInt(data.success);
+                if(status === 0) {
                     controlOld = jThis;
                 }
                 else { 
                     controlOld.prop("checked", true);
-                    if (data.success > 0)
+                    if (status > 0)
                         EventInspector_ShowMsg("Превышено максимальное кол-во баллов", "error");    
                     else 
                         EventInspector_ShowMsg("Ошибка: Изменение формы контроля", "error");
diff --git a/~dev_rating/media/js/discipline/general.js b/~dev_rating/media/js/discipline/general.js
index b2c7aa03858146c1c756d4bf7b7320267a17650c..589444dc989be15408e0265b09859023640a656b 100644
--- a/~dev_rating/media/js/discipline/general.js
+++ b/~dev_rating/media/js/discipline/general.js
@@ -3,13 +3,11 @@ var g_disciplineID;
 var g_facultyID;
 var $ = jQuery;
 $(function() {
+    var selectSubject = $("select.SelectSubject").first();
     g_URL = (window.location.href).split("/");
     g_disciplineID = g_URL[g_URL.length - 1];
-
     DisciplineID = g_disciplineID;
     
-    var subjectsGlobal = [];
-    
     // Получить список в память
     function GetSubjectsList()
     {
@@ -26,86 +24,30 @@ $(function() {
             },
             function(data)
             {
-                subjectsGlobal = (data != "") ? $.parseJSON(data) : {};
+                selectSubject.select2("val", "");
+                selectSubject.attr("disabled", true);
+                data = (data !== "") ? $.parseJSON(data) : {};
+                var jProto = $($.parseHTML("<option></option>"));
+                selectSubject.html("");
+                
+                $.each(data, function (key, cur) { 
+                    jProto.html(cur.Title).val(cur.ID);
+                    selectSubject.append(jProto.clone());	
+                });  
+                selectSubject.removeAttr("disabled");
             }
         );
     }
-    GetSubjectsList();
     
-    // Сортировка списка
-    function SortByName(nameFilter)
-    {
-        var nameFilter = nameFilter.toLowerCase();
-        var sortSubjects = {};
-        var j = 0;
-        for (var i in subjectsGlobal) { 
-            src = subjectsGlobal[i];
-            if ((src.Title).toLowerCase().indexOf(nameFilter) == 0)
-            {
-                // 0 - успех, -1 - ничего не нашел 
-                var dest = sortSubjects[j] = {};
-                dest["ID"] = src.ID;
-                dest["Title"] = src.Title;
-                j++;
-            }
-        }
-        sortSubjects.count = j;
-        
-        return sortSubjects;
+    if (selectSubject.size() > 0) {
+        selectSubject.select2({placeholder: "Выберите предмет", allowClear: true});
     }
 
-    // Для поиска предметов
-    $(".InputSubject").keyup(function()
-    {
-        var nameFilter = $(this).val();
-
-        if (nameFilter.length <= 0) {
-            $(".SelectSubject").html("");
-            return;
-        }
-
-        var sortSubjects = SortByName(nameFilter);
-        var selectSubject = $(".SelectSubject");
-        selectSubject.html("");
-        if (sortSubjects.count > 0) {
-			var jProto = $($.parseHTML("<option disabled>Выберите предмет ниже:</option>"));
-			selectSubject.append(jProto.clone());
-			jProto.removeAttr("disabled");
-            for (var i = 0; i < sortSubjects.count; ++i) {
-				jProto.html(sortSubjects[i].Title).val(sortSubjects[i].ID);
-				selectSubject.append(jProto.clone());	
-			}
-			selectSubject.attr("size", 4);
-        }
-        else
-            selectSubject.append("<option disabled>По запросу ничего не найдено.</option>").attr("size", "2");
-        selectSubject.css('display', 'block');
-
-    });
-    
-
 
     $(".SelectFaculty").change(function(){
         GetSubjectsList($(this).val());
     });
-    
-
-
-    $(".SelectSubject").change(function(){
-        $(".InputSubject").val($(this).children("option:selected").first().text());
-        $(this).hide();
-    });
-
 
-
-    $(".InputSubject").focusout(function(){
-        var selectSubject = $(".SelectSubject");
-        var nameFilter = $(this).val();
-        if (nameFilter.length == 0) {
-            selectSubject.html("");
-            selectSubject.hide();
-        }
-    });
     
 
     var jTemp = $(".HiddenInputFacultyID");
@@ -114,4 +56,4 @@ $(function() {
         //jTemp.remove(); // Тут даже Шерлок Холмс бессилен
     }
 
-})
\ No newline at end of file
+});
diff --git a/~dev_rating/media/js/getFinalForms.js b/~dev_rating/media/js/getFinalForms.js
new file mode 100644
index 0000000000000000000000000000000000000000..e0d63139d2f70b64e7f7bc305778f5f898b3730a
--- /dev/null
+++ b/~dev_rating/media/js/getFinalForms.js
@@ -0,0 +1,68 @@
+var $ = jQuery;
+$(function() {
+
+	var jDownloadStatement = $("#DownloadStatement");
+
+	//Получить список групп по ID курса
+    $("#SelectGrade").change(function() {
+        var gradeID = parseInt($(this).val()) || 0;
+        var jSelectGroup = $("#SelectGroup");
+        if (gradeID > 0)
+        {
+            $.post(
+                g_URLdir + "handler/GetData/GetGroups",
+                {   
+                    "GradeID": gradeID 
+                },
+                function(d){
+                    d = $.parseJSON(d);
+                    if(d.success === true) {
+                    	console.log(d.data);
+                    	var i = 0;
+                    	jSelectGroup.html("<option>-Не выбрана-</option>");
+                    	for (i in d.data)
+                    	{
+                    		group = d.data[i];
+                    		jSelectGroup.append("<option value='"+ group.ID +"'>Группа "+ group.GroupNum +" ("+ group.SpecAbbr +")</option>");
+                    	}
+                    	if (i > 0 )
+                    		jSelectGroup.removeAttr("disabled");
+                    }
+                }
+            );
+        }
+        else 
+        {
+        	jSelectGroup.attr("disabled", "disabled");
+        	jSelectGroup.html("<option>-Не выбрана-</option>");
+
+        	jDownloadStatement.attr("disabled", "disabled");
+        }
+    });
+
+	 $("#SelectGroup").change(function() {
+	 	var groupID = parseInt($(this).val()) || 0;
+	 	if (groupID > 0)
+	 		jDownloadStatement.removeAttr("disabled");
+	 	else jDownloadStatement.attr("disabled", "disabled");
+	 });
+
+    // Скачать ведомость
+     $('body').on('click', '#DownloadStatement', function(){
+        $.fileDownload( g_URLdir + 'handler/FileCreator/GenerateFinalFormsForGroup', {
+            httpMethod: "POST",
+            data: 
+            {
+                "GroupID": parseInt($("#SelectGroup").val()),
+                "ExamType": $("#ExamType:checked").val() 
+            },
+            successCallback: function () {
+
+            },
+            failCallback: function () {
+                EventInspector_ShowMsg('У выбранной группы отсутствуют дисциплины с зачетом', 'error');
+            }
+        });
+    });
+
+});
\ No newline at end of file
diff --git a/~dev_rating/media/js/profile.js b/~dev_rating/media/js/profile.js
index 1b9bdbda82439c7ac3f48458f900f666a4dd49c3..a950d8224020805be354c39cb73573ee389b0bc1 100644
--- a/~dev_rating/media/js/profile.js
+++ b/~dev_rating/media/js/profile.js
@@ -37,7 +37,30 @@ $(function()
         $.ajax({
             type: "POST",
             url: URLdir + "handler/Session/closeSession",
-            success: function(data) {window.location.replace(URLdir);}
+            success: function(data) {
+
+                $('.popup_overlay').css('display', 'block');
+
+                $('#signin_b').click(function()
+                { 
+
+                    $.post(URLdir + 'handler/sign/in', {'login': $('#login').val(), 'password': $.sha1($('#password').val())},
+                    function(data)
+                    {
+                        data = $.parseJSON(data);
+                        if(data.success === true)
+                        {                          
+                            $('.popup_overlay').css('display', 'none');
+                            setSessionTimer(1);
+                        }
+                        else
+                        {
+                            $('.auth_error').show();
+                        }
+                    });
+                    
+                });
+            }
         });
     }
 
@@ -52,7 +75,7 @@ $(function()
                     data = $.parseJSON(data);
                     wait = parseInt(data, 10);
                     if (wait <= 10) {
-                        closeSession(wait);
+                        closeSession();
                         return;
                     } else {
                         setSessionTimer(wait-10); 
@@ -63,33 +86,5 @@ $(function()
     }
 
     setSessionTimer(1);
-
-
-
-
-    // $(document).bind('mousemove keydown scroll', function(){
-    //     clearTimeout(timer); // отменяем прежний временной отрезок
-
-    //     timer = setTimeout(function(){ 
-    //       // Действия на отсутствие пользователя
-    //         $.ajax({
-    //             type: "POST",
-    //             url: URLdir + "handler/Sign2/getSessionTime",       
-    //             success: function(data)
-    //             { 
-    //                 data = $.parseJSON(data);
-    //                 wait2 = data.duration;
-    //             }
-    //         }); 
-            
-            
-    //         if (wait < 10) {
-    //             alert("Простите, но Вы были неактивны более " + wait/(60 * 1000) + " минут. \n\
-    //                 Если Вы хотите продолжить работу, снова войдите в систему на странице авторизации.");
-    //             document.location.href = URLdir + "/sign/out"; 
-    //         }
-    //     }, wait);
-    // });
-
-    //$("body").trigger("mousemove"); // сгенерируем ложное событие, для запуска скрипта    
+  
 });
\ No newline at end of file
diff --git a/~dev_rating/media/js/rating.js b/~dev_rating/media/js/rating.js
index 7cfac75c8140ba3f0eaf92359c44feb7309c97d1..c85ab1fdeb94fdf6f558329544ae9ece6d372eaa 100644
--- a/~dev_rating/media/js/rating.js
+++ b/~dev_rating/media/js/rating.js
@@ -2,7 +2,7 @@ var $ = jQuery;
 $(function() {
     var g_col;
     var g_row;
-    var g_isFocusCell = false; // Стоит фокус или нет
+    var g_isFocusCell = false; // Стоит фокус на ячейки или нет
     var g_oldRateCell = null;
     var g_submoduleID = null;
     var g_studentID = null;
@@ -15,11 +15,14 @@ $(function() {
     var g_URL = (window.location.href).split("/");
     var g_disciplineID = g_URL[g_URL.length - 1];
 
-    // studyGroupID для фильтра (Эффект памяти)
+    // Настройки дисциплины:
+    // + ID - id дисциплины
+    // + studyGroupID_Filter - studyGroupID для фильтра (Эффект памяти)
     var json_settings = $.parseJSON($("#hidden_div").html());
+    console.log(json_settings);
     $("#hidden_div").remove();
-    filterGroups(json_settings.studyGroupID);
-    $(".groupSelector [value='"+ json_settings.studyGroupID +"']").attr("selected", "selected");
+    filterGroups(json_settings.StudyGroupID_Filter);
+    $(".groupSelector [value='"+ json_settings.StudyGroupID_Filter +"']").attr("selected", "selected");
 
     // Скрываем все остальные группы
     // 0 - показать все
@@ -142,7 +145,7 @@ $(function() {
 
         var rateResult = parseInt(jThis.siblings(".rateResultCell").text()) - oldRate + newRate;
 
-		// exams
+		// страница сессии
 		if (jThis.attr("class").indexOf("attemptCell") >= 0)
 		{
 			//if (jThis.siblings(".semesterRateResultCell ").length > 0) {
@@ -154,7 +157,7 @@ $(function() {
 			});
 			//}
 		}
-		//end exams
+		//end страница сессии
 
         if (rateResult > 100) {
             jThis.children("input").val(oldRate);
@@ -163,6 +166,14 @@ $(function() {
         }
         else 
         {
+          	if ((json_settings.ExamType == "exam") && (rateResult - parseInt(jThis.siblings(".examCell").text())) > 60)
+          	{
+	            jThis.children("input").val(oldRate);
+	            EventInspector_ShowMsg("Сумма баллов не может привышать 60 (т.к. 40 на экзамен)", "error");
+	            jThis.children("input").removeAttr("disabled");
+	            return -1;
+          	}
+
             if (newRate <= g_submoduleMaxRate) 
             {
                 $.ajax({
diff --git a/~dev_rating/media/js/select2.js b/~dev_rating/media/js/select2.js
new file mode 100644
index 0000000000000000000000000000000000000000..7590b82295325b277441528766d83e9010f00c27
--- /dev/null
+++ b/~dev_rating/media/js/select2.js
@@ -0,0 +1,3541 @@
+/*
+Copyright 2012 Igor Vaynberg
+
+Version: 3.5.2 Timestamp: Sat Nov  1 14:43:36 EDT 2014
+
+This software is licensed under the Apache License, Version 2.0 (the "Apache License") or the GNU
+General Public License version 2 (the "GPL License"). You may choose either license to govern your
+use of this software only upon the condition that you accept all of the terms of either the Apache
+License or the GPL License.
+
+You may obtain a copy of the Apache License and the GPL License at:
+
+    http://www.apache.org/licenses/LICENSE-2.0
+    http://www.gnu.org/licenses/gpl-2.0.html
+
+Unless required by applicable law or agreed to in writing, software distributed under the
+Apache License or the GPL License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+CONDITIONS OF ANY KIND, either express or implied. See the Apache License and the GPL License for
+the specific language governing permissions and limitations under the Apache License and the GPL License.
+*/
+(function ($) {
+    if(typeof $.fn.each2 == "undefined") {
+        $.extend($.fn, {
+            /*
+            * 4-10 times faster .each replacement
+            * use it carefully, as it overrides jQuery context of element on each iteration
+            */
+            each2 : function (c) {
+                var j = $([0]), i = -1, l = this.length;
+                while (
+                    ++i < l
+                    && (j.context = j[0] = this[i])
+                    && c.call(j[0], i, j) !== false //"this"=DOM, i=index, j=jQuery object
+                );
+                return this;
+            }
+        });
+    }
+})(jQuery);
+
+(function ($, undefined) {
+    "use strict";
+    /*global document, window, jQuery, console */
+
+    if (window.Select2 !== undefined) {
+        return;
+    }
+
+    var AbstractSelect2, SingleSelect2, MultiSelect2, nextUid, sizer,
+        lastMousePosition={x:0,y:0}, $document, scrollBarDimensions,
+
+    KEY = {
+        TAB: 9,
+        ENTER: 13,
+        ESC: 27,
+        SPACE: 32,
+        LEFT: 37,
+        UP: 38,
+        RIGHT: 39,
+        DOWN: 40,
+        SHIFT: 16,
+        CTRL: 17,
+        ALT: 18,
+        PAGE_UP: 33,
+        PAGE_DOWN: 34,
+        HOME: 36,
+        END: 35,
+        BACKSPACE: 8,
+        DELETE: 46,
+        isArrow: function (k) {
+            k = k.which ? k.which : k;
+            switch (k) {
+            case KEY.LEFT:
+            case KEY.RIGHT:
+            case KEY.UP:
+            case KEY.DOWN:
+                return true;
+            }
+            return false;
+        },
+        isControl: function (e) {
+            var k = e.which;
+            switch (k) {
+            case KEY.SHIFT:
+            case KEY.CTRL:
+            case KEY.ALT:
+                return true;
+            }
+
+            if (e.metaKey) return true;
+
+            return false;
+        },
+        isFunctionKey: function (k) {
+            k = k.which ? k.which : k;
+            return k >= 112 && k <= 123;
+        }
+    },
+    MEASURE_SCROLLBAR_TEMPLATE = "<div class='select2-measure-scrollbar'></div>",
+
+    DIACRITICS = {"\u24B6":"A","\uFF21":"A","\u00C0":"A","\u00C1":"A","\u00C2":"A","\u1EA6":"A","\u1EA4":"A","\u1EAA":"A","\u1EA8":"A","\u00C3":"A","\u0100":"A","\u0102":"A","\u1EB0":"A","\u1EAE":"A","\u1EB4":"A","\u1EB2":"A","\u0226":"A","\u01E0":"A","\u00C4":"A","\u01DE":"A","\u1EA2":"A","\u00C5":"A","\u01FA":"A","\u01CD":"A","\u0200":"A","\u0202":"A","\u1EA0":"A","\u1EAC":"A","\u1EB6":"A","\u1E00":"A","\u0104":"A","\u023A":"A","\u2C6F":"A","\uA732":"AA","\u00C6":"AE","\u01FC":"AE","\u01E2":"AE","\uA734":"AO","\uA736":"AU","\uA738":"AV","\uA73A":"AV","\uA73C":"AY","\u24B7":"B","\uFF22":"B","\u1E02":"B","\u1E04":"B","\u1E06":"B","\u0243":"B","\u0182":"B","\u0181":"B","\u24B8":"C","\uFF23":"C","\u0106":"C","\u0108":"C","\u010A":"C","\u010C":"C","\u00C7":"C","\u1E08":"C","\u0187":"C","\u023B":"C","\uA73E":"C","\u24B9":"D","\uFF24":"D","\u1E0A":"D","\u010E":"D","\u1E0C":"D","\u1E10":"D","\u1E12":"D","\u1E0E":"D","\u0110":"D","\u018B":"D","\u018A":"D","\u0189":"D","\uA779":"D","\u01F1":"DZ","\u01C4":"DZ","\u01F2":"Dz","\u01C5":"Dz","\u24BA":"E","\uFF25":"E","\u00C8":"E","\u00C9":"E","\u00CA":"E","\u1EC0":"E","\u1EBE":"E","\u1EC4":"E","\u1EC2":"E","\u1EBC":"E","\u0112":"E","\u1E14":"E","\u1E16":"E","\u0114":"E","\u0116":"E","\u00CB":"E","\u1EBA":"E","\u011A":"E","\u0204":"E","\u0206":"E","\u1EB8":"E","\u1EC6":"E","\u0228":"E","\u1E1C":"E","\u0118":"E","\u1E18":"E","\u1E1A":"E","\u0190":"E","\u018E":"E","\u24BB":"F","\uFF26":"F","\u1E1E":"F","\u0191":"F","\uA77B":"F","\u24BC":"G","\uFF27":"G","\u01F4":"G","\u011C":"G","\u1E20":"G","\u011E":"G","\u0120":"G","\u01E6":"G","\u0122":"G","\u01E4":"G","\u0193":"G","\uA7A0":"G","\uA77D":"G","\uA77E":"G","\u24BD":"H","\uFF28":"H","\u0124":"H","\u1E22":"H","\u1E26":"H","\u021E":"H","\u1E24":"H","\u1E28":"H","\u1E2A":"H","\u0126":"H","\u2C67":"H","\u2C75":"H","\uA78D":"H","\u24BE":"I","\uFF29":"I","\u00CC":"I","\u00CD":"I","\u00CE":"I","\u0128":"I","\u012A":"I","\u012C":"I","\u0130":"I","\u00CF":"I","\u1E2E":"I","\u1EC8":"I","\u01CF":"I","\u0208":"I","\u020A":"I","\u1ECA":"I","\u012E":"I","\u1E2C":"I","\u0197":"I","\u24BF":"J","\uFF2A":"J","\u0134":"J","\u0248":"J","\u24C0":"K","\uFF2B":"K","\u1E30":"K","\u01E8":"K","\u1E32":"K","\u0136":"K","\u1E34":"K","\u0198":"K","\u2C69":"K","\uA740":"K","\uA742":"K","\uA744":"K","\uA7A2":"K","\u24C1":"L","\uFF2C":"L","\u013F":"L","\u0139":"L","\u013D":"L","\u1E36":"L","\u1E38":"L","\u013B":"L","\u1E3C":"L","\u1E3A":"L","\u0141":"L","\u023D":"L","\u2C62":"L","\u2C60":"L","\uA748":"L","\uA746":"L","\uA780":"L","\u01C7":"LJ","\u01C8":"Lj","\u24C2":"M","\uFF2D":"M","\u1E3E":"M","\u1E40":"M","\u1E42":"M","\u2C6E":"M","\u019C":"M","\u24C3":"N","\uFF2E":"N","\u01F8":"N","\u0143":"N","\u00D1":"N","\u1E44":"N","\u0147":"N","\u1E46":"N","\u0145":"N","\u1E4A":"N","\u1E48":"N","\u0220":"N","\u019D":"N","\uA790":"N","\uA7A4":"N","\u01CA":"NJ","\u01CB":"Nj","\u24C4":"O","\uFF2F":"O","\u00D2":"O","\u00D3":"O","\u00D4":"O","\u1ED2":"O","\u1ED0":"O","\u1ED6":"O","\u1ED4":"O","\u00D5":"O","\u1E4C":"O","\u022C":"O","\u1E4E":"O","\u014C":"O","\u1E50":"O","\u1E52":"O","\u014E":"O","\u022E":"O","\u0230":"O","\u00D6":"O","\u022A":"O","\u1ECE":"O","\u0150":"O","\u01D1":"O","\u020C":"O","\u020E":"O","\u01A0":"O","\u1EDC":"O","\u1EDA":"O","\u1EE0":"O","\u1EDE":"O","\u1EE2":"O","\u1ECC":"O","\u1ED8":"O","\u01EA":"O","\u01EC":"O","\u00D8":"O","\u01FE":"O","\u0186":"O","\u019F":"O","\uA74A":"O","\uA74C":"O","\u01A2":"OI","\uA74E":"OO","\u0222":"OU","\u24C5":"P","\uFF30":"P","\u1E54":"P","\u1E56":"P","\u01A4":"P","\u2C63":"P","\uA750":"P","\uA752":"P","\uA754":"P","\u24C6":"Q","\uFF31":"Q","\uA756":"Q","\uA758":"Q","\u024A":"Q","\u24C7":"R","\uFF32":"R","\u0154":"R","\u1E58":"R","\u0158":"R","\u0210":"R","\u0212":"R","\u1E5A":"R","\u1E5C":"R","\u0156":"R","\u1E5E":"R","\u024C":"R","\u2C64":"R","\uA75A":"R","\uA7A6":"R","\uA782":"R","\u24C8":"S","\uFF33":"S","\u1E9E":"S","\u015A":"S","\u1E64":"S","\u015C":"S","\u1E60":"S","\u0160":"S","\u1E66":"S","\u1E62":"S","\u1E68":"S","\u0218":"S","\u015E":"S","\u2C7E":"S","\uA7A8":"S","\uA784":"S","\u24C9":"T","\uFF34":"T","\u1E6A":"T","\u0164":"T","\u1E6C":"T","\u021A":"T","\u0162":"T","\u1E70":"T","\u1E6E":"T","\u0166":"T","\u01AC":"T","\u01AE":"T","\u023E":"T","\uA786":"T","\uA728":"TZ","\u24CA":"U","\uFF35":"U","\u00D9":"U","\u00DA":"U","\u00DB":"U","\u0168":"U","\u1E78":"U","\u016A":"U","\u1E7A":"U","\u016C":"U","\u00DC":"U","\u01DB":"U","\u01D7":"U","\u01D5":"U","\u01D9":"U","\u1EE6":"U","\u016E":"U","\u0170":"U","\u01D3":"U","\u0214":"U","\u0216":"U","\u01AF":"U","\u1EEA":"U","\u1EE8":"U","\u1EEE":"U","\u1EEC":"U","\u1EF0":"U","\u1EE4":"U","\u1E72":"U","\u0172":"U","\u1E76":"U","\u1E74":"U","\u0244":"U","\u24CB":"V","\uFF36":"V","\u1E7C":"V","\u1E7E":"V","\u01B2":"V","\uA75E":"V","\u0245":"V","\uA760":"VY","\u24CC":"W","\uFF37":"W","\u1E80":"W","\u1E82":"W","\u0174":"W","\u1E86":"W","\u1E84":"W","\u1E88":"W","\u2C72":"W","\u24CD":"X","\uFF38":"X","\u1E8A":"X","\u1E8C":"X","\u24CE":"Y","\uFF39":"Y","\u1EF2":"Y","\u00DD":"Y","\u0176":"Y","\u1EF8":"Y","\u0232":"Y","\u1E8E":"Y","\u0178":"Y","\u1EF6":"Y","\u1EF4":"Y","\u01B3":"Y","\u024E":"Y","\u1EFE":"Y","\u24CF":"Z","\uFF3A":"Z","\u0179":"Z","\u1E90":"Z","\u017B":"Z","\u017D":"Z","\u1E92":"Z","\u1E94":"Z","\u01B5":"Z","\u0224":"Z","\u2C7F":"Z","\u2C6B":"Z","\uA762":"Z","\u24D0":"a","\uFF41":"a","\u1E9A":"a","\u00E0":"a","\u00E1":"a","\u00E2":"a","\u1EA7":"a","\u1EA5":"a","\u1EAB":"a","\u1EA9":"a","\u00E3":"a","\u0101":"a","\u0103":"a","\u1EB1":"a","\u1EAF":"a","\u1EB5":"a","\u1EB3":"a","\u0227":"a","\u01E1":"a","\u00E4":"a","\u01DF":"a","\u1EA3":"a","\u00E5":"a","\u01FB":"a","\u01CE":"a","\u0201":"a","\u0203":"a","\u1EA1":"a","\u1EAD":"a","\u1EB7":"a","\u1E01":"a","\u0105":"a","\u2C65":"a","\u0250":"a","\uA733":"aa","\u00E6":"ae","\u01FD":"ae","\u01E3":"ae","\uA735":"ao","\uA737":"au","\uA739":"av","\uA73B":"av","\uA73D":"ay","\u24D1":"b","\uFF42":"b","\u1E03":"b","\u1E05":"b","\u1E07":"b","\u0180":"b","\u0183":"b","\u0253":"b","\u24D2":"c","\uFF43":"c","\u0107":"c","\u0109":"c","\u010B":"c","\u010D":"c","\u00E7":"c","\u1E09":"c","\u0188":"c","\u023C":"c","\uA73F":"c","\u2184":"c","\u24D3":"d","\uFF44":"d","\u1E0B":"d","\u010F":"d","\u1E0D":"d","\u1E11":"d","\u1E13":"d","\u1E0F":"d","\u0111":"d","\u018C":"d","\u0256":"d","\u0257":"d","\uA77A":"d","\u01F3":"dz","\u01C6":"dz","\u24D4":"e","\uFF45":"e","\u00E8":"e","\u00E9":"e","\u00EA":"e","\u1EC1":"e","\u1EBF":"e","\u1EC5":"e","\u1EC3":"e","\u1EBD":"e","\u0113":"e","\u1E15":"e","\u1E17":"e","\u0115":"e","\u0117":"e","\u00EB":"e","\u1EBB":"e","\u011B":"e","\u0205":"e","\u0207":"e","\u1EB9":"e","\u1EC7":"e","\u0229":"e","\u1E1D":"e","\u0119":"e","\u1E19":"e","\u1E1B":"e","\u0247":"e","\u025B":"e","\u01DD":"e","\u24D5":"f","\uFF46":"f","\u1E1F":"f","\u0192":"f","\uA77C":"f","\u24D6":"g","\uFF47":"g","\u01F5":"g","\u011D":"g","\u1E21":"g","\u011F":"g","\u0121":"g","\u01E7":"g","\u0123":"g","\u01E5":"g","\u0260":"g","\uA7A1":"g","\u1D79":"g","\uA77F":"g","\u24D7":"h","\uFF48":"h","\u0125":"h","\u1E23":"h","\u1E27":"h","\u021F":"h","\u1E25":"h","\u1E29":"h","\u1E2B":"h","\u1E96":"h","\u0127":"h","\u2C68":"h","\u2C76":"h","\u0265":"h","\u0195":"hv","\u24D8":"i","\uFF49":"i","\u00EC":"i","\u00ED":"i","\u00EE":"i","\u0129":"i","\u012B":"i","\u012D":"i","\u00EF":"i","\u1E2F":"i","\u1EC9":"i","\u01D0":"i","\u0209":"i","\u020B":"i","\u1ECB":"i","\u012F":"i","\u1E2D":"i","\u0268":"i","\u0131":"i","\u24D9":"j","\uFF4A":"j","\u0135":"j","\u01F0":"j","\u0249":"j","\u24DA":"k","\uFF4B":"k","\u1E31":"k","\u01E9":"k","\u1E33":"k","\u0137":"k","\u1E35":"k","\u0199":"k","\u2C6A":"k","\uA741":"k","\uA743":"k","\uA745":"k","\uA7A3":"k","\u24DB":"l","\uFF4C":"l","\u0140":"l","\u013A":"l","\u013E":"l","\u1E37":"l","\u1E39":"l","\u013C":"l","\u1E3D":"l","\u1E3B":"l","\u017F":"l","\u0142":"l","\u019A":"l","\u026B":"l","\u2C61":"l","\uA749":"l","\uA781":"l","\uA747":"l","\u01C9":"lj","\u24DC":"m","\uFF4D":"m","\u1E3F":"m","\u1E41":"m","\u1E43":"m","\u0271":"m","\u026F":"m","\u24DD":"n","\uFF4E":"n","\u01F9":"n","\u0144":"n","\u00F1":"n","\u1E45":"n","\u0148":"n","\u1E47":"n","\u0146":"n","\u1E4B":"n","\u1E49":"n","\u019E":"n","\u0272":"n","\u0149":"n","\uA791":"n","\uA7A5":"n","\u01CC":"nj","\u24DE":"o","\uFF4F":"o","\u00F2":"o","\u00F3":"o","\u00F4":"o","\u1ED3":"o","\u1ED1":"o","\u1ED7":"o","\u1ED5":"o","\u00F5":"o","\u1E4D":"o","\u022D":"o","\u1E4F":"o","\u014D":"o","\u1E51":"o","\u1E53":"o","\u014F":"o","\u022F":"o","\u0231":"o","\u00F6":"o","\u022B":"o","\u1ECF":"o","\u0151":"o","\u01D2":"o","\u020D":"o","\u020F":"o","\u01A1":"o","\u1EDD":"o","\u1EDB":"o","\u1EE1":"o","\u1EDF":"o","\u1EE3":"o","\u1ECD":"o","\u1ED9":"o","\u01EB":"o","\u01ED":"o","\u00F8":"o","\u01FF":"o","\u0254":"o","\uA74B":"o","\uA74D":"o","\u0275":"o","\u01A3":"oi","\u0223":"ou","\uA74F":"oo","\u24DF":"p","\uFF50":"p","\u1E55":"p","\u1E57":"p","\u01A5":"p","\u1D7D":"p","\uA751":"p","\uA753":"p","\uA755":"p","\u24E0":"q","\uFF51":"q","\u024B":"q","\uA757":"q","\uA759":"q","\u24E1":"r","\uFF52":"r","\u0155":"r","\u1E59":"r","\u0159":"r","\u0211":"r","\u0213":"r","\u1E5B":"r","\u1E5D":"r","\u0157":"r","\u1E5F":"r","\u024D":"r","\u027D":"r","\uA75B":"r","\uA7A7":"r","\uA783":"r","\u24E2":"s","\uFF53":"s","\u00DF":"s","\u015B":"s","\u1E65":"s","\u015D":"s","\u1E61":"s","\u0161":"s","\u1E67":"s","\u1E63":"s","\u1E69":"s","\u0219":"s","\u015F":"s","\u023F":"s","\uA7A9":"s","\uA785":"s","\u1E9B":"s","\u24E3":"t","\uFF54":"t","\u1E6B":"t","\u1E97":"t","\u0165":"t","\u1E6D":"t","\u021B":"t","\u0163":"t","\u1E71":"t","\u1E6F":"t","\u0167":"t","\u01AD":"t","\u0288":"t","\u2C66":"t","\uA787":"t","\uA729":"tz","\u24E4":"u","\uFF55":"u","\u00F9":"u","\u00FA":"u","\u00FB":"u","\u0169":"u","\u1E79":"u","\u016B":"u","\u1E7B":"u","\u016D":"u","\u00FC":"u","\u01DC":"u","\u01D8":"u","\u01D6":"u","\u01DA":"u","\u1EE7":"u","\u016F":"u","\u0171":"u","\u01D4":"u","\u0215":"u","\u0217":"u","\u01B0":"u","\u1EEB":"u","\u1EE9":"u","\u1EEF":"u","\u1EED":"u","\u1EF1":"u","\u1EE5":"u","\u1E73":"u","\u0173":"u","\u1E77":"u","\u1E75":"u","\u0289":"u","\u24E5":"v","\uFF56":"v","\u1E7D":"v","\u1E7F":"v","\u028B":"v","\uA75F":"v","\u028C":"v","\uA761":"vy","\u24E6":"w","\uFF57":"w","\u1E81":"w","\u1E83":"w","\u0175":"w","\u1E87":"w","\u1E85":"w","\u1E98":"w","\u1E89":"w","\u2C73":"w","\u24E7":"x","\uFF58":"x","\u1E8B":"x","\u1E8D":"x","\u24E8":"y","\uFF59":"y","\u1EF3":"y","\u00FD":"y","\u0177":"y","\u1EF9":"y","\u0233":"y","\u1E8F":"y","\u00FF":"y","\u1EF7":"y","\u1E99":"y","\u1EF5":"y","\u01B4":"y","\u024F":"y","\u1EFF":"y","\u24E9":"z","\uFF5A":"z","\u017A":"z","\u1E91":"z","\u017C":"z","\u017E":"z","\u1E93":"z","\u1E95":"z","\u01B6":"z","\u0225":"z","\u0240":"z","\u2C6C":"z","\uA763":"z","\u0386":"\u0391","\u0388":"\u0395","\u0389":"\u0397","\u038A":"\u0399","\u03AA":"\u0399","\u038C":"\u039F","\u038E":"\u03A5","\u03AB":"\u03A5","\u038F":"\u03A9","\u03AC":"\u03B1","\u03AD":"\u03B5","\u03AE":"\u03B7","\u03AF":"\u03B9","\u03CA":"\u03B9","\u0390":"\u03B9","\u03CC":"\u03BF","\u03CD":"\u03C5","\u03CB":"\u03C5","\u03B0":"\u03C5","\u03C9":"\u03C9","\u03C2":"\u03C3"};
+
+    $document = $(document);
+
+    nextUid=(function() { var counter=1; return function() { return counter++; }; }());
+
+
+    function reinsertElement(element) {
+        var placeholder = $(document.createTextNode(''));
+
+        element.before(placeholder);
+        placeholder.before(element);
+        placeholder.remove();
+    }
+
+    function stripDiacritics(str) {
+        // Used 'uni range + named function' from http://jsperf.com/diacritics/18
+        function match(a) {
+            return DIACRITICS[a] || a;
+        }
+
+        return str.replace(/[^\u0000-\u007E]/g, match);
+    }
+
+    function indexOf(value, array) {
+        var i = 0, l = array.length;
+        for (; i < l; i = i + 1) {
+            if (equal(value, array[i])) return i;
+        }
+        return -1;
+    }
+
+    function measureScrollbar () {
+        var $template = $( MEASURE_SCROLLBAR_TEMPLATE );
+        $template.appendTo(document.body);
+
+        var dim = {
+            width: $template.width() - $template[0].clientWidth,
+            height: $template.height() - $template[0].clientHeight
+        };
+        $template.remove();
+
+        return dim;
+    }
+
+    /**
+     * Compares equality of a and b
+     * @param a
+     * @param b
+     */
+    function equal(a, b) {
+        if (a === b) return true;
+        if (a === undefined || b === undefined) return false;
+        if (a === null || b === null) return false;
+        // Check whether 'a' or 'b' is a string (primitive or object).
+        // The concatenation of an empty string (+'') converts its argument to a string's primitive.
+        if (a.constructor === String) return a+'' === b+''; // a+'' - in case 'a' is a String object
+        if (b.constructor === String) return b+'' === a+''; // b+'' - in case 'b' is a String object
+        return false;
+    }
+
+    /**
+     * Splits the string into an array of values, transforming each value. An empty array is returned for nulls or empty
+     * strings
+     * @param string
+     * @param separator
+     */
+    function splitVal(string, separator, transform) {
+        var val, i, l;
+        if (string === null || string.length < 1) return [];
+        val = string.split(separator);
+        for (i = 0, l = val.length; i < l; i = i + 1) val[i] = transform(val[i]);
+        return val;
+    }
+
+    function getSideBorderPadding(element) {
+        return element.outerWidth(false) - element.width();
+    }
+
+    function installKeyUpChangeEvent(element) {
+        var key="keyup-change-value";
+        element.on("keydown", function () {
+            if ($.data(element, key) === undefined) {
+                $.data(element, key, element.val());
+            }
+        });
+        element.on("keyup", function () {
+            var val= $.data(element, key);
+            if (val !== undefined && element.val() !== val) {
+                $.removeData(element, key);
+                element.trigger("keyup-change");
+            }
+        });
+    }
+
+
+    /**
+     * filters mouse events so an event is fired only if the mouse moved.
+     *
+     * filters out mouse events that occur when mouse is stationary but
+     * the elements under the pointer are scrolled.
+     */
+    function installFilteredMouseMove(element) {
+        element.on("mousemove", function (e) {
+            var lastpos = lastMousePosition;
+            if (lastpos === undefined || lastpos.x !== e.pageX || lastpos.y !== e.pageY) {
+                $(e.target).trigger("mousemove-filtered", e);
+            }
+        });
+    }
+
+    /**
+     * Debounces a function. Returns a function that calls the original fn function only if no invocations have been made
+     * within the last quietMillis milliseconds.
+     *
+     * @param quietMillis number of milliseconds to wait before invoking fn
+     * @param fn function to be debounced
+     * @param ctx object to be used as this reference within fn
+     * @return debounced version of fn
+     */
+    function debounce(quietMillis, fn, ctx) {
+        ctx = ctx || undefined;
+        var timeout;
+        return function () {
+            var args = arguments;
+            window.clearTimeout(timeout);
+            timeout = window.setTimeout(function() {
+                fn.apply(ctx, args);
+            }, quietMillis);
+        };
+    }
+
+    function installDebouncedScroll(threshold, element) {
+        var notify = debounce(threshold, function (e) { element.trigger("scroll-debounced", e);});
+        element.on("scroll", function (e) {
+            if (indexOf(e.target, element.get()) >= 0) notify(e);
+        });
+    }
+
+    function focus($el) {
+        if ($el[0] === document.activeElement) return;
+
+        /* set the focus in a 0 timeout - that way the focus is set after the processing
+            of the current event has finished - which seems like the only reliable way
+            to set focus */
+        window.setTimeout(function() {
+            var el=$el[0], pos=$el.val().length, range;
+
+            $el.focus();
+
+            /* make sure el received focus so we do not error out when trying to manipulate the caret.
+                sometimes modals or others listeners may steal it after its set */
+            var isVisible = (el.offsetWidth > 0 || el.offsetHeight > 0);
+            if (isVisible && el === document.activeElement) {
+
+                /* after the focus is set move the caret to the end, necessary when we val()
+                    just before setting focus */
+                if(el.setSelectionRange)
+                {
+                    el.setSelectionRange(pos, pos);
+                }
+                else if (el.createTextRange) {
+                    range = el.createTextRange();
+                    range.collapse(false);
+                    range.select();
+                }
+            }
+        }, 0);
+    }
+
+    function getCursorInfo(el) {
+        el = $(el)[0];
+        var offset = 0;
+        var length = 0;
+        if ('selectionStart' in el) {
+            offset = el.selectionStart;
+            length = el.selectionEnd - offset;
+        } else if ('selection' in document) {
+            el.focus();
+            var sel = document.selection.createRange();
+            length = document.selection.createRange().text.length;
+            sel.moveStart('character', -el.value.length);
+            offset = sel.text.length - length;
+        }
+        return { offset: offset, length: length };
+    }
+
+    function killEvent(event) {
+        event.preventDefault();
+        event.stopPropagation();
+    }
+    function killEventImmediately(event) {
+        event.preventDefault();
+        event.stopImmediatePropagation();
+    }
+
+    function measureTextWidth(e) {
+        if (!sizer){
+            var style = e[0].currentStyle || window.getComputedStyle(e[0], null);
+            sizer = $(document.createElement("div")).css({
+                position: "absolute",
+                left: "-10000px",
+                top: "-10000px",
+                display: "none",
+                fontSize: style.fontSize,
+                fontFamily: style.fontFamily,
+                fontStyle: style.fontStyle,
+                fontWeight: style.fontWeight,
+                letterSpacing: style.letterSpacing,
+                textTransform: style.textTransform,
+                whiteSpace: "nowrap"
+            });
+            sizer.attr("class","select2-sizer");
+            $(document.body).append(sizer);
+        }
+        sizer.text(e.val());
+        return sizer.width();
+    }
+
+    function syncCssClasses(dest, src, adapter) {
+        var classes, replacements = [], adapted;
+
+        classes = $.trim(dest.attr("class"));
+
+        if (classes) {
+            classes = '' + classes; // for IE which returns object
+
+            $(classes.split(/\s+/)).each2(function() {
+                if (this.indexOf("select2-") === 0) {
+                    replacements.push(this);
+                }
+            });
+        }
+
+        classes = $.trim(src.attr("class"));
+
+        if (classes) {
+            classes = '' + classes; // for IE which returns object
+
+            $(classes.split(/\s+/)).each2(function() {
+                if (this.indexOf("select2-") !== 0) {
+                    adapted = adapter(this);
+
+                    if (adapted) {
+                        replacements.push(adapted);
+                    }
+                }
+            });
+        }
+
+        dest.attr("class", replacements.join(" "));
+    }
+
+
+    function markMatch(text, term, markup, escapeMarkup) {
+        var match=stripDiacritics(text.toUpperCase()).indexOf(stripDiacritics(term.toUpperCase())),
+            tl=term.length;
+
+        if (match<0) {
+            markup.push(escapeMarkup(text));
+            return;
+        }
+
+        markup.push(escapeMarkup(text.substring(0, match)));
+        markup.push("<span class='select2-match'>");
+        markup.push(escapeMarkup(text.substring(match, match + tl)));
+        markup.push("</span>");
+        markup.push(escapeMarkup(text.substring(match + tl, text.length)));
+    }
+
+    function defaultEscapeMarkup(markup) {
+        var replace_map = {
+            '\\': '&#92;',
+            '&': '&amp;',
+            '<': '&lt;',
+            '>': '&gt;',
+            '"': '&quot;',
+            "'": '&#39;',
+            "/": '&#47;'
+        };
+
+        return String(markup).replace(/[&<>"'\/\\]/g, function (match) {
+            return replace_map[match];
+        });
+    }
+
+    /**
+     * Produces an ajax-based query function
+     *
+     * @param options object containing configuration parameters
+     * @param options.params parameter map for the transport ajax call, can contain such options as cache, jsonpCallback, etc. see $.ajax
+     * @param options.transport function that will be used to execute the ajax request. must be compatible with parameters supported by $.ajax
+     * @param options.url url for the data
+     * @param options.data a function(searchTerm, pageNumber, context) that should return an object containing query string parameters for the above url.
+     * @param options.dataType request data type: ajax, jsonp, other datatypes supported by jQuery's $.ajax function or the transport function if specified
+     * @param options.quietMillis (optional) milliseconds to wait before making the ajaxRequest, helps debounce the ajax function if invoked too often
+     * @param options.results a function(remoteData, pageNumber, query) that converts data returned form the remote request to the format expected by Select2.
+     *      The expected format is an object containing the following keys:
+     *      results array of objects that will be used as choices
+     *      more (optional) boolean indicating whether there are more results available
+     *      Example: {results:[{id:1, text:'Red'},{id:2, text:'Blue'}], more:true}
+     */
+    function ajax(options) {
+        var timeout, // current scheduled but not yet executed request
+            handler = null,
+            quietMillis = options.quietMillis || 100,
+            ajaxUrl = options.url,
+            self = this;
+
+        return function (query) {
+            window.clearTimeout(timeout);
+            timeout = window.setTimeout(function () {
+                var data = options.data, // ajax data function
+                    url = ajaxUrl, // ajax url string or function
+                    transport = options.transport || $.fn.select2.ajaxDefaults.transport,
+                    // deprecated - to be removed in 4.0  - use params instead
+                    deprecated = {
+                        type: options.type || 'GET', // set type of request (GET or POST)
+                        cache: options.cache || false,
+                        jsonpCallback: options.jsonpCallback||undefined,
+                        dataType: options.dataType||"json"
+                    },
+                    params = $.extend({}, $.fn.select2.ajaxDefaults.params, deprecated);
+
+                data = data ? data.call(self, query.term, query.page, query.context) : null;
+                url = (typeof url === 'function') ? url.call(self, query.term, query.page, query.context) : url;
+
+                if (handler && typeof handler.abort === "function") { handler.abort(); }
+
+                if (options.params) {
+                    if ($.isFunction(options.params)) {
+                        $.extend(params, options.params.call(self));
+                    } else {
+                        $.extend(params, options.params);
+                    }
+                }
+
+                $.extend(params, {
+                    url: url,
+                    dataType: options.dataType,
+                    data: data,
+                    success: function (data) {
+                        // TODO - replace query.page with query so users have access to term, page, etc.
+                        // added query as third paramter to keep backwards compatibility
+                        var results = options.results(data, query.page, query);
+                        query.callback(results);
+                    },
+                    error: function(jqXHR, textStatus, errorThrown){
+                        var results = {
+                            hasError: true,
+                            jqXHR: jqXHR,
+                            textStatus: textStatus,
+                            errorThrown: errorThrown
+                        };
+
+                        query.callback(results);
+                    }
+                });
+                handler = transport.call(self, params);
+            }, quietMillis);
+        };
+    }
+
+    /**
+     * Produces a query function that works with a local array
+     *
+     * @param options object containing configuration parameters. The options parameter can either be an array or an
+     * object.
+     *
+     * If the array form is used it is assumed that it contains objects with 'id' and 'text' keys.
+     *
+     * If the object form is used it is assumed that it contains 'data' and 'text' keys. The 'data' key should contain
+     * an array of objects that will be used as choices. These objects must contain at least an 'id' key. The 'text'
+     * key can either be a String in which case it is expected that each element in the 'data' array has a key with the
+     * value of 'text' which will be used to match choices. Alternatively, text can be a function(item) that can extract
+     * the text.
+     */
+    function local(options) {
+        var data = options, // data elements
+            dataText,
+            tmp,
+            text = function (item) { return ""+item.text; }; // function used to retrieve the text portion of a data item that is matched against the search
+
+         if ($.isArray(data)) {
+            tmp = data;
+            data = { results: tmp };
+        }
+
+         if ($.isFunction(data) === false) {
+            tmp = data;
+            data = function() { return tmp; };
+        }
+
+        var dataItem = data();
+        if (dataItem.text) {
+            text = dataItem.text;
+            // if text is not a function we assume it to be a key name
+            if (!$.isFunction(text)) {
+                dataText = dataItem.text; // we need to store this in a separate variable because in the next step data gets reset and data.text is no longer available
+                text = function (item) { return item[dataText]; };
+            }
+        }
+
+        return function (query) {
+            var t = query.term, filtered = { results: [] }, process;
+            if (t === "") {
+                query.callback(data());
+                return;
+            }
+
+            process = function(datum, collection) {
+                var group, attr;
+                datum = datum[0];
+                if (datum.children) {
+                    group = {};
+                    for (attr in datum) {
+                        if (datum.hasOwnProperty(attr)) group[attr]=datum[attr];
+                    }
+                    group.children=[];
+                    $(datum.children).each2(function(i, childDatum) { process(childDatum, group.children); });
+                    if (group.children.length || query.matcher(t, text(group), datum)) {
+                        collection.push(group);
+                    }
+                } else {
+                    if (query.matcher(t, text(datum), datum)) {
+                        collection.push(datum);
+                    }
+                }
+            };
+
+            $(data().results).each2(function(i, datum) { process(datum, filtered.results); });
+            query.callback(filtered);
+        };
+    }
+
+    // TODO javadoc
+    function tags(data) {
+        var isFunc = $.isFunction(data);
+        return function (query) {
+            var t = query.term, filtered = {results: []};
+            var result = isFunc ? data(query) : data;
+            if ($.isArray(result)) {
+                $(result).each(function () {
+                    var isObject = this.text !== undefined,
+                        text = isObject ? this.text : this;
+                    if (t === "" || query.matcher(t, text)) {
+                        filtered.results.push(isObject ? this : {id: this, text: this});
+                    }
+                });
+                query.callback(filtered);
+            }
+        };
+    }
+
+    /**
+     * Checks if the formatter function should be used.
+     *
+     * Throws an error if it is not a function. Returns true if it should be used,
+     * false if no formatting should be performed.
+     *
+     * @param formatter
+     */
+    function checkFormatter(formatter, formatterName) {
+        if ($.isFunction(formatter)) return true;
+        if (!formatter) return false;
+        if (typeof(formatter) === 'string') return true;
+        throw new Error(formatterName +" must be a string, function, or falsy value");
+    }
+
+  /**
+   * Returns a given value
+   * If given a function, returns its output
+   *
+   * @param val string|function
+   * @param context value of "this" to be passed to function
+   * @returns {*}
+   */
+    function evaluate(val, context) {
+        if ($.isFunction(val)) {
+            var args = Array.prototype.slice.call(arguments, 2);
+            return val.apply(context, args);
+        }
+        return val;
+    }
+
+    function countResults(results) {
+        var count = 0;
+        $.each(results, function(i, item) {
+            if (item.children) {
+                count += countResults(item.children);
+            } else {
+                count++;
+            }
+        });
+        return count;
+    }
+
+    /**
+     * Default tokenizer. This function uses breaks the input on substring match of any string from the
+     * opts.tokenSeparators array and uses opts.createSearchChoice to create the choice object. Both of those
+     * two options have to be defined in order for the tokenizer to work.
+     *
+     * @param input text user has typed so far or pasted into the search field
+     * @param selection currently selected choices
+     * @param selectCallback function(choice) callback tho add the choice to selection
+     * @param opts select2's opts
+     * @return undefined/null to leave the current input unchanged, or a string to change the input to the returned value
+     */
+    function defaultTokenizer(input, selection, selectCallback, opts) {
+        var original = input, // store the original so we can compare and know if we need to tell the search to update its text
+            dupe = false, // check for whether a token we extracted represents a duplicate selected choice
+            token, // token
+            index, // position at which the separator was found
+            i, l, // looping variables
+            separator; // the matched separator
+
+        if (!opts.createSearchChoice || !opts.tokenSeparators || opts.tokenSeparators.length < 1) return undefined;
+
+        while (true) {
+            index = -1;
+
+            for (i = 0, l = opts.tokenSeparators.length; i < l; i++) {
+                separator = opts.tokenSeparators[i];
+                index = input.indexOf(separator);
+                if (index >= 0) break;
+            }
+
+            if (index < 0) break; // did not find any token separator in the input string, bail
+
+            token = input.substring(0, index);
+            input = input.substring(index + separator.length);
+
+            if (token.length > 0) {
+                token = opts.createSearchChoice.call(this, token, selection);
+                if (token !== undefined && token !== null && opts.id(token) !== undefined && opts.id(token) !== null) {
+                    dupe = false;
+                    for (i = 0, l = selection.length; i < l; i++) {
+                        if (equal(opts.id(token), opts.id(selection[i]))) {
+                            dupe = true; break;
+                        }
+                    }
+
+                    if (!dupe) selectCallback(token);
+                }
+            }
+        }
+
+        if (original!==input) return input;
+    }
+
+    function cleanupJQueryElements() {
+        var self = this;
+
+        $.each(arguments, function (i, element) {
+            self[element].remove();
+            self[element] = null;
+        });
+    }
+
+    /**
+     * Creates a new class
+     *
+     * @param superClass
+     * @param methods
+     */
+    function clazz(SuperClass, methods) {
+        var constructor = function () {};
+        constructor.prototype = new SuperClass;
+        constructor.prototype.constructor = constructor;
+        constructor.prototype.parent = SuperClass.prototype;
+        constructor.prototype = $.extend(constructor.prototype, methods);
+        return constructor;
+    }
+
+    AbstractSelect2 = clazz(Object, {
+
+        // abstract
+        bind: function (func) {
+            var self = this;
+            return function () {
+                func.apply(self, arguments);
+            };
+        },
+
+        // abstract
+        init: function (opts) {
+            var results, search, resultsSelector = ".select2-results";
+
+            // prepare options
+            this.opts = opts = this.prepareOpts(opts);
+
+            this.id=opts.id;
+
+            // destroy if called on an existing component
+            if (opts.element.data("select2") !== undefined &&
+                opts.element.data("select2") !== null) {
+                opts.element.data("select2").destroy();
+            }
+
+            this.container = this.createContainer();
+
+            this.liveRegion = $('.select2-hidden-accessible');
+            if (this.liveRegion.length == 0) {
+                this.liveRegion = $("<span>", {
+                        role: "status",
+                        "aria-live": "polite"
+                    })
+                    .addClass("select2-hidden-accessible")
+                    .appendTo(document.body);
+            }
+
+            this.containerId="s2id_"+(opts.element.attr("id") || "autogen"+nextUid());
+            this.containerEventName= this.containerId
+                .replace(/([.])/g, '_')
+                .replace(/([;&,\-\.\+\*\~':"\!\^#$%@\[\]\(\)=>\|])/g, '\\$1');
+            this.container.attr("id", this.containerId);
+
+            this.container.attr("title", opts.element.attr("title"));
+
+            this.body = $(document.body);
+
+            syncCssClasses(this.container, this.opts.element, this.opts.adaptContainerCssClass);
+
+            this.container.attr("style", opts.element.attr("style"));
+            this.container.css(evaluate(opts.containerCss, this.opts.element));
+            this.container.addClass(evaluate(opts.containerCssClass, this.opts.element));
+
+            this.elementTabIndex = this.opts.element.attr("tabindex");
+
+            // swap container for the element
+            this.opts.element
+                .data("select2", this)
+                .attr("tabindex", "-1")
+                .before(this.container)
+                .on("click.select2", killEvent); // do not leak click events
+
+            this.container.data("select2", this);
+
+            this.dropdown = this.container.find(".select2-drop");
+
+            syncCssClasses(this.dropdown, this.opts.element, this.opts.adaptDropdownCssClass);
+
+            this.dropdown.addClass(evaluate(opts.dropdownCssClass, this.opts.element));
+            this.dropdown.data("select2", this);
+            this.dropdown.on("click", killEvent);
+
+            this.results = results = this.container.find(resultsSelector);
+            this.search = search = this.container.find("input.select2-input");
+
+            this.queryCount = 0;
+            this.resultsPage = 0;
+            this.context = null;
+
+            // initialize the container
+            this.initContainer();
+
+            this.container.on("click", killEvent);
+
+            installFilteredMouseMove(this.results);
+
+            this.dropdown.on("mousemove-filtered", resultsSelector, this.bind(this.highlightUnderEvent));
+            this.dropdown.on("touchstart touchmove touchend", resultsSelector, this.bind(function (event) {
+                this._touchEvent = true;
+                this.highlightUnderEvent(event);
+            }));
+            this.dropdown.on("touchmove", resultsSelector, this.bind(this.touchMoved));
+            this.dropdown.on("touchstart touchend", resultsSelector, this.bind(this.clearTouchMoved));
+
+            // Waiting for a click event on touch devices to select option and hide dropdown
+            // otherwise click will be triggered on an underlying element
+            this.dropdown.on('click', this.bind(function (event) {
+                if (this._touchEvent) {
+                    this._touchEvent = false;
+                    this.selectHighlighted();
+                }
+            }));
+
+            installDebouncedScroll(80, this.results);
+            this.dropdown.on("scroll-debounced", resultsSelector, this.bind(this.loadMoreIfNeeded));
+
+            // do not propagate change event from the search field out of the component
+            $(this.container).on("change", ".select2-input", function(e) {e.stopPropagation();});
+            $(this.dropdown).on("change", ".select2-input", function(e) {e.stopPropagation();});
+
+            // if jquery.mousewheel plugin is installed we can prevent out-of-bounds scrolling of results via mousewheel
+            if ($.fn.mousewheel) {
+                results.mousewheel(function (e, delta, deltaX, deltaY) {
+                    var top = results.scrollTop();
+                    if (deltaY > 0 && top - deltaY <= 0) {
+                        results.scrollTop(0);
+                        killEvent(e);
+                    } else if (deltaY < 0 && results.get(0).scrollHeight - results.scrollTop() + deltaY <= results.height()) {
+                        results.scrollTop(results.get(0).scrollHeight - results.height());
+                        killEvent(e);
+                    }
+                });
+            }
+
+            installKeyUpChangeEvent(search);
+            search.on("keyup-change input paste", this.bind(this.updateResults));
+            search.on("focus", function () { search.addClass("select2-focused"); });
+            search.on("blur", function () { search.removeClass("select2-focused");});
+
+            this.dropdown.on("mouseup", resultsSelector, this.bind(function (e) {
+                if ($(e.target).closest(".select2-result-selectable").length > 0) {
+                    this.highlightUnderEvent(e);
+                    this.selectHighlighted(e);
+                }
+            }));
+
+            // trap all mouse events from leaving the dropdown. sometimes there may be a modal that is listening
+            // for mouse events outside of itself so it can close itself. since the dropdown is now outside the select2's
+            // dom it will trigger the popup close, which is not what we want
+            // focusin can cause focus wars between modals and select2 since the dropdown is outside the modal.
+            this.dropdown.on("click mouseup mousedown touchstart touchend focusin", function (e) { e.stopPropagation(); });
+
+            this.nextSearchTerm = undefined;
+
+            if ($.isFunction(this.opts.initSelection)) {
+                // initialize selection based on the current value of the source element
+                this.initSelection();
+
+                // if the user has provided a function that can set selection based on the value of the source element
+                // we monitor the change event on the element and trigger it, allowing for two way synchronization
+                this.monitorSource();
+            }
+
+            if (opts.maximumInputLength !== null) {
+                this.search.attr("maxlength", opts.maximumInputLength);
+            }
+
+            var disabled = opts.element.prop("disabled");
+            if (disabled === undefined) disabled = false;
+            this.enable(!disabled);
+
+            var readonly = opts.element.prop("readonly");
+            if (readonly === undefined) readonly = false;
+            this.readonly(readonly);
+
+            // Calculate size of scrollbar
+            scrollBarDimensions = scrollBarDimensions || measureScrollbar();
+
+            this.autofocus = opts.element.prop("autofocus");
+            opts.element.prop("autofocus", false);
+            if (this.autofocus) this.focus();
+
+            this.search.attr("placeholder", opts.searchInputPlaceholder);
+        },
+
+        // abstract
+        destroy: function () {
+            var element=this.opts.element, select2 = element.data("select2"), self = this;
+
+            this.close();
+
+            if (element.length && element[0].detachEvent && self._sync) {
+                element.each(function () {
+                    if (self._sync) {
+                        this.detachEvent("onpropertychange", self._sync);
+                    }
+                });
+            }
+            if (this.propertyObserver) {
+                this.propertyObserver.disconnect();
+                this.propertyObserver = null;
+            }
+            this._sync = null;
+
+            if (select2 !== undefined) {
+                select2.container.remove();
+                select2.liveRegion.remove();
+                select2.dropdown.remove();
+                element
+                    .show()
+                    .removeData("select2")
+                    .off(".select2")
+                    .prop("autofocus", this.autofocus || false);
+                if (this.elementTabIndex) {
+                    element.attr({tabindex: this.elementTabIndex});
+                } else {
+                    element.removeAttr("tabindex");
+                }
+                element.show();
+            }
+
+            cleanupJQueryElements.call(this,
+                "container",
+                "liveRegion",
+                "dropdown",
+                "results",
+                "search"
+            );
+        },
+
+        // abstract
+        optionToData: function(element) {
+            if (element.is("option")) {
+                return {
+                    id:element.prop("value"),
+                    text:element.text(),
+                    element: element.get(),
+                    css: element.attr("class"),
+                    disabled: element.prop("disabled"),
+                    locked: equal(element.attr("locked"), "locked") || equal(element.data("locked"), true)
+                };
+            } else if (element.is("optgroup")) {
+                return {
+                    text:element.attr("label"),
+                    children:[],
+                    element: element.get(),
+                    css: element.attr("class")
+                };
+            }
+        },
+
+        // abstract
+        prepareOpts: function (opts) {
+            var element, select, idKey, ajaxUrl, self = this;
+
+            element = opts.element;
+
+            if (element.get(0).tagName.toLowerCase() === "select") {
+                this.select = select = opts.element;
+            }
+
+            if (select) {
+                // these options are not allowed when attached to a select because they are picked up off the element itself
+                $.each(["id", "multiple", "ajax", "query", "createSearchChoice", "initSelection", "data", "tags"], function () {
+                    if (this in opts) {
+                        throw new Error("Option '" + this + "' is not allowed for Select2 when attached to a <select> element.");
+                    }
+                });
+            }
+
+            opts = $.extend({}, {
+                populateResults: function(container, results, query) {
+                    var populate, id=this.opts.id, liveRegion=this.liveRegion;
+
+                    populate=function(results, container, depth) {
+
+                        var i, l, result, selectable, disabled, compound, node, label, innerContainer, formatted;
+
+                        results = opts.sortResults(results, container, query);
+
+                        // collect the created nodes for bulk append
+                        var nodes = [];
+                        for (i = 0, l = results.length; i < l; i = i + 1) {
+
+                            result=results[i];
+
+                            disabled = (result.disabled === true);
+                            selectable = (!disabled) && (id(result) !== undefined);
+
+                            compound=result.children && result.children.length > 0;
+
+                            node=$("<li></li>");
+                            node.addClass("select2-results-dept-"+depth);
+                            node.addClass("select2-result");
+                            node.addClass(selectable ? "select2-result-selectable" : "select2-result-unselectable");
+                            if (disabled) { node.addClass("select2-disabled"); }
+                            if (compound) { node.addClass("select2-result-with-children"); }
+                            node.addClass(self.opts.formatResultCssClass(result));
+                            node.attr("role", "presentation");
+
+                            label=$(document.createElement("div"));
+                            label.addClass("select2-result-label");
+                            label.attr("id", "select2-result-label-" + nextUid());
+                            label.attr("role", "option");
+
+                            formatted=opts.formatResult(result, label, query, self.opts.escapeMarkup);
+                            if (formatted!==undefined) {
+                                label.html(formatted);
+                                node.append(label);
+                            }
+
+
+                            if (compound) {
+
+                                innerContainer=$("<ul></ul>");
+                                innerContainer.addClass("select2-result-sub");
+                                populate(result.children, innerContainer, depth+1);
+                                node.append(innerContainer);
+                            }
+
+                            node.data("select2-data", result);
+                            nodes.push(node[0]);
+                        }
+
+                        // bulk append the created nodes
+                        container.append(nodes);
+                        liveRegion.text(opts.formatMatches(results.length));
+                    };
+
+                    populate(results, container, 0);
+                }
+            }, $.fn.select2.defaults, opts);
+
+            if (typeof(opts.id) !== "function") {
+                idKey = opts.id;
+                opts.id = function (e) { return e[idKey]; };
+            }
+
+            if ($.isArray(opts.element.data("select2Tags"))) {
+                if ("tags" in opts) {
+                    throw "tags specified as both an attribute 'data-select2-tags' and in options of Select2 " + opts.element.attr("id");
+                }
+                opts.tags=opts.element.data("select2Tags");
+            }
+
+            if (select) {
+                opts.query = this.bind(function (query) {
+                    var data = { results: [], more: false },
+                        term = query.term,
+                        children, placeholderOption, process;
+
+                    process=function(element, collection) {
+                        var group;
+                        if (element.is("option")) {
+                            if (query.matcher(term, element.text(), element)) {
+                                collection.push(self.optionToData(element));
+                            }
+                        } else if (element.is("optgroup")) {
+                            group=self.optionToData(element);
+                            element.children().each2(function(i, elm) { process(elm, group.children); });
+                            if (group.children.length>0) {
+                                collection.push(group);
+                            }
+                        }
+                    };
+
+                    children=element.children();
+
+                    // ignore the placeholder option if there is one
+                    if (this.getPlaceholder() !== undefined && children.length > 0) {
+                        placeholderOption = this.getPlaceholderOption();
+                        if (placeholderOption) {
+                            children=children.not(placeholderOption);
+                        }
+                    }
+
+                    children.each2(function(i, elm) { process(elm, data.results); });
+
+                    query.callback(data);
+                });
+                // this is needed because inside val() we construct choices from options and their id is hardcoded
+                opts.id=function(e) { return e.id; };
+            } else {
+                if (!("query" in opts)) {
+
+                    if ("ajax" in opts) {
+                        ajaxUrl = opts.element.data("ajax-url");
+                        if (ajaxUrl && ajaxUrl.length > 0) {
+                            opts.ajax.url = ajaxUrl;
+                        }
+                        opts.query = ajax.call(opts.element, opts.ajax);
+                    } else if ("data" in opts) {
+                        opts.query = local(opts.data);
+                    } else if ("tags" in opts) {
+                        opts.query = tags(opts.tags);
+                        if (opts.createSearchChoice === undefined) {
+                            opts.createSearchChoice = function (term) { return {id: $.trim(term), text: $.trim(term)}; };
+                        }
+                        if (opts.initSelection === undefined) {
+                            opts.initSelection = function (element, callback) {
+                                var data = [];
+                                $(splitVal(element.val(), opts.separator, opts.transformVal)).each(function () {
+                                    var obj = { id: this, text: this },
+                                        tags = opts.tags;
+                                    if ($.isFunction(tags)) tags=tags();
+                                    $(tags).each(function() { if (equal(this.id, obj.id)) { obj = this; return false; } });
+                                    data.push(obj);
+                                });
+
+                                callback(data);
+                            };
+                        }
+                    }
+                }
+            }
+            if (typeof(opts.query) !== "function") {
+                throw "query function not defined for Select2 " + opts.element.attr("id");
+            }
+
+            if (opts.createSearchChoicePosition === 'top') {
+                opts.createSearchChoicePosition = function(list, item) { list.unshift(item); };
+            }
+            else if (opts.createSearchChoicePosition === 'bottom') {
+                opts.createSearchChoicePosition = function(list, item) { list.push(item); };
+            }
+            else if (typeof(opts.createSearchChoicePosition) !== "function")  {
+                throw "invalid createSearchChoicePosition option must be 'top', 'bottom' or a custom function";
+            }
+
+            return opts;
+        },
+
+        /**
+         * Monitor the original element for changes and update select2 accordingly
+         */
+        // abstract
+        monitorSource: function () {
+            var el = this.opts.element, observer, self = this;
+
+            el.on("change.select2", this.bind(function (e) {
+                if (this.opts.element.data("select2-change-triggered") !== true) {
+                    this.initSelection();
+                }
+            }));
+
+            this._sync = this.bind(function () {
+
+                // sync enabled state
+                var disabled = el.prop("disabled");
+                if (disabled === undefined) disabled = false;
+                this.enable(!disabled);
+
+                var readonly = el.prop("readonly");
+                if (readonly === undefined) readonly = false;
+                this.readonly(readonly);
+
+                if (this.container) {
+                    syncCssClasses(this.container, this.opts.element, this.opts.adaptContainerCssClass);
+                    this.container.addClass(evaluate(this.opts.containerCssClass, this.opts.element));
+                }
+
+                if (this.dropdown) {
+                    syncCssClasses(this.dropdown, this.opts.element, this.opts.adaptDropdownCssClass);
+                    this.dropdown.addClass(evaluate(this.opts.dropdownCssClass, this.opts.element));
+                }
+
+            });
+
+            // IE8-10 (IE9/10 won't fire propertyChange via attachEventListener)
+            if (el.length && el[0].attachEvent) {
+                el.each(function() {
+                    this.attachEvent("onpropertychange", self._sync);
+                });
+            }
+
+            // safari, chrome, firefox, IE11
+            observer = window.MutationObserver || window.WebKitMutationObserver|| window.MozMutationObserver;
+            if (observer !== undefined) {
+                if (this.propertyObserver) { delete this.propertyObserver; this.propertyObserver = null; }
+                this.propertyObserver = new observer(function (mutations) {
+                    $.each(mutations, self._sync);
+                });
+                this.propertyObserver.observe(el.get(0), { attributes:true, subtree:false });
+            }
+        },
+
+        // abstract
+        triggerSelect: function(data) {
+            var evt = $.Event("select2-selecting", { val: this.id(data), object: data, choice: data });
+            this.opts.element.trigger(evt);
+            return !evt.isDefaultPrevented();
+        },
+
+        /**
+         * Triggers the change event on the source element
+         */
+        // abstract
+        triggerChange: function (details) {
+
+            details = details || {};
+            details= $.extend({}, details, { type: "change", val: this.val() });
+            // prevents recursive triggering
+            this.opts.element.data("select2-change-triggered", true);
+            this.opts.element.trigger(details);
+            this.opts.element.data("select2-change-triggered", false);
+
+            // some validation frameworks ignore the change event and listen instead to keyup, click for selects
+            // so here we trigger the click event manually
+            this.opts.element.click();
+
+            // ValidationEngine ignores the change event and listens instead to blur
+            // so here we trigger the blur event manually if so desired
+            if (this.opts.blurOnChange)
+                this.opts.element.blur();
+        },
+
+        //abstract
+        isInterfaceEnabled: function()
+        {
+            return this.enabledInterface === true;
+        },
+
+        // abstract
+        enableInterface: function() {
+            var enabled = this._enabled && !this._readonly,
+                disabled = !enabled;
+
+            if (enabled === this.enabledInterface) return false;
+
+            this.container.toggleClass("select2-container-disabled", disabled);
+            this.close();
+            this.enabledInterface = enabled;
+
+            return true;
+        },
+
+        // abstract
+        enable: function(enabled) {
+            if (enabled === undefined) enabled = true;
+            if (this._enabled === enabled) return;
+            this._enabled = enabled;
+
+            this.opts.element.prop("disabled", !enabled);
+            this.enableInterface();
+        },
+
+        // abstract
+        disable: function() {
+            this.enable(false);
+        },
+
+        // abstract
+        readonly: function(enabled) {
+            if (enabled === undefined) enabled = false;
+            if (this._readonly === enabled) return;
+            this._readonly = enabled;
+
+            this.opts.element.prop("readonly", enabled);
+            this.enableInterface();
+        },
+
+        // abstract
+        opened: function () {
+            return (this.container) ? this.container.hasClass("select2-dropdown-open") : false;
+        },
+
+        // abstract
+        positionDropdown: function() {
+            var $dropdown = this.dropdown,
+                container = this.container,
+                offset = container.offset(),
+                height = container.outerHeight(false),
+                width = container.outerWidth(false),
+                dropHeight = $dropdown.outerHeight(false),
+                $window = $(window),
+                windowWidth = $window.width(),
+                windowHeight = $window.height(),
+                viewPortRight = $window.scrollLeft() + windowWidth,
+                viewportBottom = $window.scrollTop() + windowHeight,
+                dropTop = offset.top + height,
+                dropLeft = offset.left,
+                enoughRoomBelow = dropTop + dropHeight <= viewportBottom,
+                enoughRoomAbove = (offset.top - dropHeight) >= $window.scrollTop(),
+                dropWidth = $dropdown.outerWidth(false),
+                enoughRoomOnRight = function() {
+                    return dropLeft + dropWidth <= viewPortRight;
+                },
+                enoughRoomOnLeft = function() {
+                    return offset.left + viewPortRight + container.outerWidth(false)  > dropWidth;
+                },
+                aboveNow = $dropdown.hasClass("select2-drop-above"),
+                bodyOffset,
+                above,
+                changeDirection,
+                css,
+                resultsListNode;
+
+            // always prefer the current above/below alignment, unless there is not enough room
+            if (aboveNow) {
+                above = true;
+                if (!enoughRoomAbove && enoughRoomBelow) {
+                    changeDirection = true;
+                    above = false;
+                }
+            } else {
+                above = false;
+                if (!enoughRoomBelow && enoughRoomAbove) {
+                    changeDirection = true;
+                    above = true;
+                }
+            }
+
+            //if we are changing direction we need to get positions when dropdown is hidden;
+            if (changeDirection) {
+                $dropdown.hide();
+                offset = this.container.offset();
+                height = this.container.outerHeight(false);
+                width = this.container.outerWidth(false);
+                dropHeight = $dropdown.outerHeight(false);
+                viewPortRight = $window.scrollLeft() + windowWidth;
+                viewportBottom = $window.scrollTop() + windowHeight;
+                dropTop = offset.top + height;
+                dropLeft = offset.left;
+                dropWidth = $dropdown.outerWidth(false);
+                $dropdown.show();
+
+                // fix so the cursor does not move to the left within the search-textbox in IE
+                this.focusSearch();
+            }
+
+            if (this.opts.dropdownAutoWidth) {
+                resultsListNode = $('.select2-results', $dropdown)[0];
+                $dropdown.addClass('select2-drop-auto-width');
+                $dropdown.css('width', '');
+                // Add scrollbar width to dropdown if vertical scrollbar is present
+                dropWidth = $dropdown.outerWidth(false) + (resultsListNode.scrollHeight === resultsListNode.clientHeight ? 0 : scrollBarDimensions.width);
+                dropWidth > width ? width = dropWidth : dropWidth = width;
+                dropHeight = $dropdown.outerHeight(false);
+            }
+            else {
+                this.container.removeClass('select2-drop-auto-width');
+            }
+
+            //console.log("below/ droptop:", dropTop, "dropHeight", dropHeight, "sum", (dropTop+dropHeight)+" viewport bottom", viewportBottom, "enough?", enoughRoomBelow);
+            //console.log("above/ offset.top", offset.top, "dropHeight", dropHeight, "top", (offset.top-dropHeight), "scrollTop", this.body.scrollTop(), "enough?", enoughRoomAbove);
+
+            // fix positioning when body has an offset and is not position: static
+            if (this.body.css('position') !== 'static') {
+                bodyOffset = this.body.offset();
+                dropTop -= bodyOffset.top;
+                dropLeft -= bodyOffset.left;
+            }
+
+            if (!enoughRoomOnRight() && enoughRoomOnLeft()) {
+                dropLeft = offset.left + this.container.outerWidth(false) - dropWidth;
+            }
+
+            css =  {
+                left: dropLeft,
+                width: width
+            };
+
+            if (above) {
+                css.top = offset.top - dropHeight;
+                css.bottom = 'auto';
+                this.container.addClass("select2-drop-above");
+                $dropdown.addClass("select2-drop-above");
+            }
+            else {
+                css.top = dropTop;
+                css.bottom = 'auto';
+                this.container.removeClass("select2-drop-above");
+                $dropdown.removeClass("select2-drop-above");
+            }
+            css = $.extend(css, evaluate(this.opts.dropdownCss, this.opts.element));
+
+            $dropdown.css(css);
+        },
+
+        // abstract
+        shouldOpen: function() {
+            var event;
+
+            if (this.opened()) return false;
+
+            if (this._enabled === false || this._readonly === true) return false;
+
+            event = $.Event("select2-opening");
+            this.opts.element.trigger(event);
+            return !event.isDefaultPrevented();
+        },
+
+        // abstract
+        clearDropdownAlignmentPreference: function() {
+            // clear the classes used to figure out the preference of where the dropdown should be opened
+            this.container.removeClass("select2-drop-above");
+            this.dropdown.removeClass("select2-drop-above");
+        },
+
+        /**
+         * Opens the dropdown
+         *
+         * @return {Boolean} whether or not dropdown was opened. This method will return false if, for example,
+         * the dropdown is already open, or if the 'open' event listener on the element called preventDefault().
+         */
+        // abstract
+        open: function () {
+
+            if (!this.shouldOpen()) return false;
+
+            this.opening();
+
+            // Only bind the document mousemove when the dropdown is visible
+            $document.on("mousemove.select2Event", function (e) {
+                lastMousePosition.x = e.pageX;
+                lastMousePosition.y = e.pageY;
+            });
+
+            return true;
+        },
+
+        /**
+         * Performs the opening of the dropdown
+         */
+        // abstract
+        opening: function() {
+            var cid = this.containerEventName,
+                scroll = "scroll." + cid,
+                resize = "resize."+cid,
+                orient = "orientationchange."+cid,
+                mask;
+
+            this.container.addClass("select2-dropdown-open").addClass("select2-container-active");
+
+            this.clearDropdownAlignmentPreference();
+
+            if(this.dropdown[0] !== this.body.children().last()[0]) {
+                this.dropdown.detach().appendTo(this.body);
+            }
+
+            // create the dropdown mask if doesn't already exist
+            mask = $("#select2-drop-mask");
+            if (mask.length === 0) {
+                mask = $(document.createElement("div"));
+                mask.attr("id","select2-drop-mask").attr("class","select2-drop-mask");
+                mask.hide();
+                mask.appendTo(this.body);
+                mask.on("mousedown touchstart click", function (e) {
+                    // Prevent IE from generating a click event on the body
+                    reinsertElement(mask);
+
+                    var dropdown = $("#select2-drop"), self;
+                    if (dropdown.length > 0) {
+                        self=dropdown.data("select2");
+                        if (self.opts.selectOnBlur) {
+                            self.selectHighlighted({noFocus: true});
+                        }
+                        self.close();
+                        e.preventDefault();
+                        e.stopPropagation();
+                    }
+                });
+            }
+
+            // ensure the mask is always right before the dropdown
+            if (this.dropdown.prev()[0] !== mask[0]) {
+                this.dropdown.before(mask);
+            }
+
+            // move the global id to the correct dropdown
+            $("#select2-drop").removeAttr("id");
+            this.dropdown.attr("id", "select2-drop");
+
+            // show the elements
+            mask.show();
+
+            this.positionDropdown();
+            this.dropdown.show();
+            this.positionDropdown();
+
+            this.dropdown.addClass("select2-drop-active");
+
+            // attach listeners to events that can change the position of the container and thus require
+            // the position of the dropdown to be updated as well so it does not come unglued from the container
+            var that = this;
+            this.container.parents().add(window).each(function () {
+                $(this).on(resize+" "+scroll+" "+orient, function (e) {
+                    if (that.opened()) that.positionDropdown();
+                });
+            });
+
+
+        },
+
+        // abstract
+        close: function () {
+            if (!this.opened()) return;
+
+            var cid = this.containerEventName,
+                scroll = "scroll." + cid,
+                resize = "resize."+cid,
+                orient = "orientationchange."+cid;
+
+            // unbind event listeners
+            this.container.parents().add(window).each(function () { $(this).off(scroll).off(resize).off(orient); });
+
+            this.clearDropdownAlignmentPreference();
+
+            $("#select2-drop-mask").hide();
+            this.dropdown.removeAttr("id"); // only the active dropdown has the select2-drop id
+            this.dropdown.hide();
+            this.container.removeClass("select2-dropdown-open").removeClass("select2-container-active");
+            this.results.empty();
+
+            // Now that the dropdown is closed, unbind the global document mousemove event
+            $document.off("mousemove.select2Event");
+
+            this.clearSearch();
+            this.search.removeClass("select2-active");
+            this.opts.element.trigger($.Event("select2-close"));
+        },
+
+        /**
+         * Opens control, sets input value, and updates results.
+         */
+        // abstract
+        externalSearch: function (term) {
+            this.open();
+            this.search.val(term);
+            this.updateResults(false);
+        },
+
+        // abstract
+        clearSearch: function () {
+
+        },
+
+        //abstract
+        getMaximumSelectionSize: function() {
+            return evaluate(this.opts.maximumSelectionSize, this.opts.element);
+        },
+
+        // abstract
+        ensureHighlightVisible: function () {
+            var results = this.results, children, index, child, hb, rb, y, more, topOffset;
+
+            index = this.highlight();
+
+            if (index < 0) return;
+
+            if (index == 0) {
+
+                // if the first element is highlighted scroll all the way to the top,
+                // that way any unselectable headers above it will also be scrolled
+                // into view
+
+                results.scrollTop(0);
+                return;
+            }
+
+            children = this.findHighlightableChoices().find('.select2-result-label');
+
+            child = $(children[index]);
+
+            topOffset = (child.offset() || {}).top || 0;
+
+            hb = topOffset + child.outerHeight(true);
+
+            // if this is the last child lets also make sure select2-more-results is visible
+            if (index === children.length - 1) {
+                more = results.find("li.select2-more-results");
+                if (more.length > 0) {
+                    hb = more.offset().top + more.outerHeight(true);
+                }
+            }
+
+            rb = results.offset().top + results.outerHeight(false);
+            if (hb > rb) {
+                results.scrollTop(results.scrollTop() + (hb - rb));
+            }
+            y = topOffset - results.offset().top;
+
+            // make sure the top of the element is visible
+            if (y < 0 && child.css('display') != 'none' ) {
+                results.scrollTop(results.scrollTop() + y); // y is negative
+            }
+        },
+
+        // abstract
+        findHighlightableChoices: function() {
+            return this.results.find(".select2-result-selectable:not(.select2-disabled):not(.select2-selected)");
+        },
+
+        // abstract
+        moveHighlight: function (delta) {
+            var choices = this.findHighlightableChoices(),
+                index = this.highlight();
+
+            while (index > -1 && index < choices.length) {
+                index += delta;
+                var choice = $(choices[index]);
+                if (choice.hasClass("select2-result-selectable") && !choice.hasClass("select2-disabled") && !choice.hasClass("select2-selected")) {
+                    this.highlight(index);
+                    break;
+                }
+            }
+        },
+
+        // abstract
+        highlight: function (index) {
+            var choices = this.findHighlightableChoices(),
+                choice,
+                data;
+
+            if (arguments.length === 0) {
+                return indexOf(choices.filter(".select2-highlighted")[0], choices.get());
+            }
+
+            if (index >= choices.length) index = choices.length - 1;
+            if (index < 0) index = 0;
+
+            this.removeHighlight();
+
+            choice = $(choices[index]);
+            choice.addClass("select2-highlighted");
+
+            // ensure assistive technology can determine the active choice
+            this.search.attr("aria-activedescendant", choice.find(".select2-result-label").attr("id"));
+
+            this.ensureHighlightVisible();
+
+            this.liveRegion.text(choice.text());
+
+            data = choice.data("select2-data");
+            if (data) {
+                this.opts.element.trigger({ type: "select2-highlight", val: this.id(data), choice: data });
+            }
+        },
+
+        removeHighlight: function() {
+            this.results.find(".select2-highlighted").removeClass("select2-highlighted");
+        },
+
+        touchMoved: function() {
+            this._touchMoved = true;
+        },
+
+        clearTouchMoved: function() {
+          this._touchMoved = false;
+        },
+
+        // abstract
+        countSelectableResults: function() {
+            return this.findHighlightableChoices().length;
+        },
+
+        // abstract
+        highlightUnderEvent: function (event) {
+            var el = $(event.target).closest(".select2-result-selectable");
+            if (el.length > 0 && !el.is(".select2-highlighted")) {
+                var choices = this.findHighlightableChoices();
+                this.highlight(choices.index(el));
+            } else if (el.length == 0) {
+                // if we are over an unselectable item remove all highlights
+                this.removeHighlight();
+            }
+        },
+
+        // abstract
+        loadMoreIfNeeded: function () {
+            var results = this.results,
+                more = results.find("li.select2-more-results"),
+                below, // pixels the element is below the scroll fold, below==0 is when the element is starting to be visible
+                page = this.resultsPage + 1,
+                self=this,
+                term=this.search.val(),
+                context=this.context;
+
+            if (more.length === 0) return;
+            below = more.offset().top - results.offset().top - results.height();
+
+            if (below <= this.opts.loadMorePadding) {
+                more.addClass("select2-active");
+                this.opts.query({
+                        element: this.opts.element,
+                        term: term,
+                        page: page,
+                        context: context,
+                        matcher: this.opts.matcher,
+                        callback: this.bind(function (data) {
+
+                    // ignore a response if the select2 has been closed before it was received
+                    if (!self.opened()) return;
+
+
+                    self.opts.populateResults.call(this, results, data.results, {term: term, page: page, context:context});
+                    self.postprocessResults(data, false, false);
+
+                    if (data.more===true) {
+                        more.detach().appendTo(results).html(self.opts.escapeMarkup(evaluate(self.opts.formatLoadMore, self.opts.element, page+1)));
+                        window.setTimeout(function() { self.loadMoreIfNeeded(); }, 10);
+                    } else {
+                        more.remove();
+                    }
+                    self.positionDropdown();
+                    self.resultsPage = page;
+                    self.context = data.context;
+                    this.opts.element.trigger({ type: "select2-loaded", items: data });
+                })});
+            }
+        },
+
+        /**
+         * Default tokenizer function which does nothing
+         */
+        tokenize: function() {
+
+        },
+
+        /**
+         * @param initial whether or not this is the call to this method right after the dropdown has been opened
+         */
+        // abstract
+        updateResults: function (initial) {
+            var search = this.search,
+                results = this.results,
+                opts = this.opts,
+                data,
+                self = this,
+                input,
+                term = search.val(),
+                lastTerm = $.data(this.container, "select2-last-term"),
+                // sequence number used to drop out-of-order responses
+                queryNumber;
+
+            // prevent duplicate queries against the same term
+            if (initial !== true && lastTerm && equal(term, lastTerm)) return;
+
+            $.data(this.container, "select2-last-term", term);
+
+            // if the search is currently hidden we do not alter the results
+            if (initial !== true && (this.showSearchInput === false || !this.opened())) {
+                return;
+            }
+
+            function postRender() {
+                search.removeClass("select2-active");
+                self.positionDropdown();
+                if (results.find('.select2-no-results,.select2-selection-limit,.select2-searching').length) {
+                    self.liveRegion.text(results.text());
+                }
+                else {
+                    self.liveRegion.text(self.opts.formatMatches(results.find('.select2-result-selectable:not(".select2-selected")').length));
+                }
+            }
+
+            function render(html) {
+                results.html(html);
+                postRender();
+            }
+
+            queryNumber = ++this.queryCount;
+
+            var maxSelSize = this.getMaximumSelectionSize();
+            if (maxSelSize >=1) {
+                data = this.data();
+                if ($.isArray(data) && data.length >= maxSelSize && checkFormatter(opts.formatSelectionTooBig, "formatSelectionTooBig")) {
+                    render("<li class='select2-selection-limit'>" + evaluate(opts.formatSelectionTooBig, opts.element, maxSelSize) + "</li>");
+                    return;
+                }
+            }
+
+            if (search.val().length < opts.minimumInputLength) {
+                if (checkFormatter(opts.formatInputTooShort, "formatInputTooShort")) {
+                    render("<li class='select2-no-results'>" + evaluate(opts.formatInputTooShort, opts.element, search.val(), opts.minimumInputLength) + "</li>");
+                } else {
+                    render("");
+                }
+                if (initial && this.showSearch) this.showSearch(true);
+                return;
+            }
+
+            if (opts.maximumInputLength && search.val().length > opts.maximumInputLength) {
+                if (checkFormatter(opts.formatInputTooLong, "formatInputTooLong")) {
+                    render("<li class='select2-no-results'>" + evaluate(opts.formatInputTooLong, opts.element, search.val(), opts.maximumInputLength) + "</li>");
+                } else {
+                    render("");
+                }
+                return;
+            }
+
+            if (opts.formatSearching && this.findHighlightableChoices().length === 0) {
+                render("<li class='select2-searching'>" + evaluate(opts.formatSearching, opts.element) + "</li>");
+            }
+
+            search.addClass("select2-active");
+
+            this.removeHighlight();
+
+            // give the tokenizer a chance to pre-process the input
+            input = this.tokenize();
+            if (input != undefined && input != null) {
+                search.val(input);
+            }
+
+            this.resultsPage = 1;
+
+            opts.query({
+                element: opts.element,
+                    term: search.val(),
+                    page: this.resultsPage,
+                    context: null,
+                    matcher: opts.matcher,
+                    callback: this.bind(function (data) {
+                var def; // default choice
+
+                // ignore old responses
+                if (queryNumber != this.queryCount) {
+                  return;
+                }
+
+                // ignore a response if the select2 has been closed before it was received
+                if (!this.opened()) {
+                    this.search.removeClass("select2-active");
+                    return;
+                }
+
+                // handle ajax error
+                if(data.hasError !== undefined && checkFormatter(opts.formatAjaxError, "formatAjaxError")) {
+                    render("<li class='select2-ajax-error'>" + evaluate(opts.formatAjaxError, opts.element, data.jqXHR, data.textStatus, data.errorThrown) + "</li>");
+                    return;
+                }
+
+                // save context, if any
+                this.context = (data.context===undefined) ? null : data.context;
+                // create a default choice and prepend it to the list
+                if (this.opts.createSearchChoice && search.val() !== "") {
+                    def = this.opts.createSearchChoice.call(self, search.val(), data.results);
+                    if (def !== undefined && def !== null && self.id(def) !== undefined && self.id(def) !== null) {
+                        if ($(data.results).filter(
+                            function () {
+                                return equal(self.id(this), self.id(def));
+                            }).length === 0) {
+                            this.opts.createSearchChoicePosition(data.results, def);
+                        }
+                    }
+                }
+
+                if (data.results.length === 0 && checkFormatter(opts.formatNoMatches, "formatNoMatches")) {
+                    render("<li class='select2-no-results'>" + evaluate(opts.formatNoMatches, opts.element, search.val()) + "</li>");
+                    return;
+                }
+
+                results.empty();
+                self.opts.populateResults.call(this, results, data.results, {term: search.val(), page: this.resultsPage, context:null});
+
+                if (data.more === true && checkFormatter(opts.formatLoadMore, "formatLoadMore")) {
+                    results.append("<li class='select2-more-results'>" + opts.escapeMarkup(evaluate(opts.formatLoadMore, opts.element, this.resultsPage)) + "</li>");
+                    window.setTimeout(function() { self.loadMoreIfNeeded(); }, 10);
+                }
+
+                this.postprocessResults(data, initial);
+
+                postRender();
+
+                this.opts.element.trigger({ type: "select2-loaded", items: data });
+            })});
+        },
+
+        // abstract
+        cancel: function () {
+            this.close();
+        },
+
+        // abstract
+        blur: function () {
+            // if selectOnBlur == true, select the currently highlighted option
+            if (this.opts.selectOnBlur)
+                this.selectHighlighted({noFocus: true});
+
+            this.close();
+            this.container.removeClass("select2-container-active");
+            // synonymous to .is(':focus'), which is available in jquery >= 1.6
+            if (this.search[0] === document.activeElement) { this.search.blur(); }
+            this.clearSearch();
+            this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus");
+        },
+
+        // abstract
+        focusSearch: function () {
+            focus(this.search);
+        },
+
+        // abstract
+        selectHighlighted: function (options) {
+            if (this._touchMoved) {
+              this.clearTouchMoved();
+              return;
+            }
+            var index=this.highlight(),
+                highlighted=this.results.find(".select2-highlighted"),
+                data = highlighted.closest('.select2-result').data("select2-data");
+
+            if (data) {
+                this.highlight(index);
+                this.onSelect(data, options);
+            } else if (options && options.noFocus) {
+                this.close();
+            }
+        },
+
+        // abstract
+        getPlaceholder: function () {
+            var placeholderOption;
+            return this.opts.element.attr("placeholder") ||
+                this.opts.element.attr("data-placeholder") || // jquery 1.4 compat
+                this.opts.element.data("placeholder") ||
+                this.opts.placeholder ||
+                ((placeholderOption = this.getPlaceholderOption()) !== undefined ? placeholderOption.text() : undefined);
+        },
+
+        // abstract
+        getPlaceholderOption: function() {
+            if (this.select) {
+                var firstOption = this.select.children('option').first();
+                if (this.opts.placeholderOption !== undefined ) {
+                    //Determine the placeholder option based on the specified placeholderOption setting
+                    return (this.opts.placeholderOption === "first" && firstOption) ||
+                           (typeof this.opts.placeholderOption === "function" && this.opts.placeholderOption(this.select));
+                } else if ($.trim(firstOption.text()) === "" && firstOption.val() === "") {
+                    //No explicit placeholder option specified, use the first if it's blank
+                    return firstOption;
+                }
+            }
+        },
+
+        /**
+         * Get the desired width for the container element.  This is
+         * derived first from option `width` passed to select2, then
+         * the inline 'style' on the original element, and finally
+         * falls back to the jQuery calculated element width.
+         */
+        // abstract
+        initContainerWidth: function () {
+            function resolveContainerWidth() {
+                var style, attrs, matches, i, l, attr;
+
+                if (this.opts.width === "off") {
+                    return null;
+                } else if (this.opts.width === "element"){
+                    return this.opts.element.outerWidth(false) === 0 ? 'auto' : this.opts.element.outerWidth(false) + 'px';
+                } else if (this.opts.width === "copy" || this.opts.width === "resolve") {
+                    // check if there is inline style on the element that contains width
+                    style = this.opts.element.attr('style');
+                    if (style !== undefined) {
+                        attrs = style.split(';');
+                        for (i = 0, l = attrs.length; i < l; i = i + 1) {
+                            attr = attrs[i].replace(/\s/g, '');
+                            matches = attr.match(/^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i);
+                            if (matches !== null && matches.length >= 1)
+                                return matches[1];
+                        }
+                    }
+
+                    if (this.opts.width === "resolve") {
+                        // next check if css('width') can resolve a width that is percent based, this is sometimes possible
+                        // when attached to input type=hidden or elements hidden via css
+                        style = this.opts.element.css('width');
+                        if (style.indexOf("%") > 0) return style;
+
+                        // finally, fallback on the calculated width of the element
+                        return (this.opts.element.outerWidth(false) === 0 ? 'auto' : this.opts.element.outerWidth(false) + 'px');
+                    }
+
+                    return null;
+                } else if ($.isFunction(this.opts.width)) {
+                    return this.opts.width();
+                } else {
+                    return this.opts.width;
+               }
+            };
+
+            var width = resolveContainerWidth.call(this);
+            if (width !== null) {
+                this.container.css("width", width);
+            }
+        }
+    });
+
+    SingleSelect2 = clazz(AbstractSelect2, {
+
+        // single
+
+        createContainer: function () {
+            var container = $(document.createElement("div")).attr({
+                "class": "select2-container"
+            }).html([
+                "<a href='javascript:void(0)' class='select2-choice' tabindex='-1'>",
+                "   <span class='select2-chosen'>&#160;</span><abbr class='select2-search-choice-close'></abbr>",
+                "   <span class='select2-arrow' role='presentation'><b role='presentation'></b></span>",
+                "</a>",
+                "<label for='' class='select2-offscreen'></label>",
+                "<input class='select2-focusser select2-offscreen' type='text' aria-haspopup='true' role='button' />",
+                "<div class='select2-drop select2-display-none'>",
+                "   <div class='select2-search'>",
+                "       <label for='' class='select2-offscreen'></label>",
+                "       <input type='text' autocomplete='off' autocorrect='off' autocapitalize='off' spellcheck='false' class='select2-input' role='combobox' aria-expanded='true'",
+                "       aria-autocomplete='list' />",
+                "   </div>",
+                "   <ul class='select2-results' role='listbox'>",
+                "   </ul>",
+                "</div>"].join(""));
+            return container;
+        },
+
+        // single
+        enableInterface: function() {
+            if (this.parent.enableInterface.apply(this, arguments)) {
+                this.focusser.prop("disabled", !this.isInterfaceEnabled());
+            }
+        },
+
+        // single
+        opening: function () {
+            var el, range, len;
+
+            if (this.opts.minimumResultsForSearch >= 0) {
+                this.showSearch(true);
+            }
+
+            this.parent.opening.apply(this, arguments);
+
+            if (this.showSearchInput !== false) {
+                // IE appends focusser.val() at the end of field :/ so we manually insert it at the beginning using a range
+                // all other browsers handle this just fine
+
+                this.search.val(this.focusser.val());
+            }
+            if (this.opts.shouldFocusInput(this)) {
+                this.search.focus();
+                // move the cursor to the end after focussing, otherwise it will be at the beginning and
+                // new text will appear *before* focusser.val()
+                el = this.search.get(0);
+                if (el.createTextRange) {
+                    range = el.createTextRange();
+                    range.collapse(false);
+                    range.select();
+                } else if (el.setSelectionRange) {
+                    len = this.search.val().length;
+                    el.setSelectionRange(len, len);
+                }
+            }
+
+            // initializes search's value with nextSearchTerm (if defined by user)
+            // ignore nextSearchTerm if the dropdown is opened by the user pressing a letter
+            if(this.search.val() === "") {
+                if(this.nextSearchTerm != undefined){
+                    this.search.val(this.nextSearchTerm);
+                    this.search.select();
+                }
+            }
+
+            this.focusser.prop("disabled", true).val("");
+            this.updateResults(true);
+            this.opts.element.trigger($.Event("select2-open"));
+        },
+
+        // single
+        close: function () {
+            if (!this.opened()) return;
+            this.parent.close.apply(this, arguments);
+
+            this.focusser.prop("disabled", false);
+
+            if (this.opts.shouldFocusInput(this)) {
+                this.focusser.focus();
+            }
+        },
+
+        // single
+        focus: function () {
+            if (this.opened()) {
+                this.close();
+            } else {
+                this.focusser.prop("disabled", false);
+                if (this.opts.shouldFocusInput(this)) {
+                    this.focusser.focus();
+                }
+            }
+        },
+
+        // single
+        isFocused: function () {
+            return this.container.hasClass("select2-container-active");
+        },
+
+        // single
+        cancel: function () {
+            this.parent.cancel.apply(this, arguments);
+            this.focusser.prop("disabled", false);
+
+            if (this.opts.shouldFocusInput(this)) {
+                this.focusser.focus();
+            }
+        },
+
+        // single
+        destroy: function() {
+            $("label[for='" + this.focusser.attr('id') + "']")
+                .attr('for', this.opts.element.attr("id"));
+            this.parent.destroy.apply(this, arguments);
+
+            cleanupJQueryElements.call(this,
+                "selection",
+                "focusser"
+            );
+        },
+
+        // single
+        initContainer: function () {
+
+            var selection,
+                container = this.container,
+                dropdown = this.dropdown,
+                idSuffix = nextUid(),
+                elementLabel;
+
+            if (this.opts.minimumResultsForSearch < 0) {
+                this.showSearch(false);
+            } else {
+                this.showSearch(true);
+            }
+
+            this.selection = selection = container.find(".select2-choice");
+
+            this.focusser = container.find(".select2-focusser");
+
+            // add aria associations
+            selection.find(".select2-chosen").attr("id", "select2-chosen-"+idSuffix);
+            this.focusser.attr("aria-labelledby", "select2-chosen-"+idSuffix);
+            this.results.attr("id", "select2-results-"+idSuffix);
+            this.search.attr("aria-owns", "select2-results-"+idSuffix);
+
+            // rewrite labels from original element to focusser
+            this.focusser.attr("id", "s2id_autogen"+idSuffix);
+
+            elementLabel = $("label[for='" + this.opts.element.attr("id") + "']");
+            this.opts.element.focus(this.bind(function () { this.focus(); }));
+
+            this.focusser.prev()
+                .text(elementLabel.text())
+                .attr('for', this.focusser.attr('id'));
+
+            // Ensure the original element retains an accessible name
+            var originalTitle = this.opts.element.attr("title");
+            this.opts.element.attr("title", (originalTitle || elementLabel.text()));
+
+            this.focusser.attr("tabindex", this.elementTabIndex);
+
+            // write label for search field using the label from the focusser element
+            this.search.attr("id", this.focusser.attr('id') + '_search');
+
+            this.search.prev()
+                .text($("label[for='" + this.focusser.attr('id') + "']").text())
+                .attr('for', this.search.attr('id'));
+
+            this.search.on("keydown", this.bind(function (e) {
+                if (!this.isInterfaceEnabled()) return;
+
+                // filter 229 keyCodes (input method editor is processing key input)
+                if (229 == e.keyCode) return;
+
+                if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) {
+                    // prevent the page from scrolling
+                    killEvent(e);
+                    return;
+                }
+
+                switch (e.which) {
+                    case KEY.UP:
+                    case KEY.DOWN:
+                        this.moveHighlight((e.which === KEY.UP) ? -1 : 1);
+                        killEvent(e);
+                        return;
+                    case KEY.ENTER:
+                        this.selectHighlighted();
+                        killEvent(e);
+                        return;
+                    case KEY.TAB:
+                        this.selectHighlighted({noFocus: true});
+                        return;
+                    case KEY.ESC:
+                        this.cancel(e);
+                        killEvent(e);
+                        return;
+                }
+            }));
+
+            this.search.on("blur", this.bind(function(e) {
+                // a workaround for chrome to keep the search field focussed when the scroll bar is used to scroll the dropdown.
+                // without this the search field loses focus which is annoying
+                if (document.activeElement === this.body.get(0)) {
+                    window.setTimeout(this.bind(function() {
+                        if (this.opened()) {
+                            this.search.focus();
+                        }
+                    }), 0);
+                }
+            }));
+
+            this.focusser.on("keydown", this.bind(function (e) {
+                if (!this.isInterfaceEnabled()) return;
+
+                if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) {
+                    return;
+                }
+
+                if (this.opts.openOnEnter === false && e.which === KEY.ENTER) {
+                    killEvent(e);
+                    return;
+                }
+
+                if (e.which == KEY.DOWN || e.which == KEY.UP
+                    || (e.which == KEY.ENTER && this.opts.openOnEnter)) {
+
+                    if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) return;
+
+                    this.open();
+                    killEvent(e);
+                    return;
+                }
+
+                if (e.which == KEY.DELETE || e.which == KEY.BACKSPACE) {
+                    if (this.opts.allowClear) {
+                        this.clear();
+                    }
+                    killEvent(e);
+                    return;
+                }
+            }));
+
+
+            installKeyUpChangeEvent(this.focusser);
+            this.focusser.on("keyup-change input", this.bind(function(e) {
+                if (this.opts.minimumResultsForSearch >= 0) {
+                    e.stopPropagation();
+                    if (this.opened()) return;
+                    this.open();
+                }
+            }));
+
+            selection.on("mousedown touchstart", "abbr", this.bind(function (e) {
+                if (!this.isInterfaceEnabled()) {
+                    return;
+                }
+
+                this.clear();
+                killEventImmediately(e);
+                this.close();
+
+                if (this.selection) {
+                    this.selection.focus();
+                }
+            }));
+
+            selection.on("mousedown touchstart", this.bind(function (e) {
+                // Prevent IE from generating a click event on the body
+                reinsertElement(selection);
+
+                if (!this.container.hasClass("select2-container-active")) {
+                    this.opts.element.trigger($.Event("select2-focus"));
+                }
+
+                if (this.opened()) {
+                    this.close();
+                } else if (this.isInterfaceEnabled()) {
+                    this.open();
+                }
+
+                killEvent(e);
+            }));
+
+            dropdown.on("mousedown touchstart", this.bind(function() {
+                if (this.opts.shouldFocusInput(this)) {
+                    this.search.focus();
+                }
+            }));
+
+            selection.on("focus", this.bind(function(e) {
+                killEvent(e);
+            }));
+
+            this.focusser.on("focus", this.bind(function(){
+                if (!this.container.hasClass("select2-container-active")) {
+                    this.opts.element.trigger($.Event("select2-focus"));
+                }
+                this.container.addClass("select2-container-active");
+            })).on("blur", this.bind(function() {
+                if (!this.opened()) {
+                    this.container.removeClass("select2-container-active");
+                    this.opts.element.trigger($.Event("select2-blur"));
+                }
+            }));
+            this.search.on("focus", this.bind(function(){
+                if (!this.container.hasClass("select2-container-active")) {
+                    this.opts.element.trigger($.Event("select2-focus"));
+                }
+                this.container.addClass("select2-container-active");
+            }));
+
+            this.initContainerWidth();
+            this.opts.element.hide();
+            this.setPlaceholder();
+
+        },
+
+        // single
+        clear: function(triggerChange) {
+            var data=this.selection.data("select2-data");
+            if (data) { // guard against queued quick consecutive clicks
+                var evt = $.Event("select2-clearing");
+                this.opts.element.trigger(evt);
+                if (evt.isDefaultPrevented()) {
+                    return;
+                }
+                var placeholderOption = this.getPlaceholderOption();
+                this.opts.element.val(placeholderOption ? placeholderOption.val() : "");
+                this.selection.find(".select2-chosen").empty();
+                this.selection.removeData("select2-data");
+                this.setPlaceholder();
+
+                if (triggerChange !== false){
+                    this.opts.element.trigger({ type: "select2-removed", val: this.id(data), choice: data });
+                    this.triggerChange({removed:data});
+                }
+            }
+        },
+
+        /**
+         * Sets selection based on source element's value
+         */
+        // single
+        initSelection: function () {
+            var selected;
+            if (this.isPlaceholderOptionSelected()) {
+                this.updateSelection(null);
+                this.close();
+                this.setPlaceholder();
+            } else {
+                var self = this;
+                this.opts.initSelection.call(null, this.opts.element, function(selected){
+                    if (selected !== undefined && selected !== null) {
+                        self.updateSelection(selected);
+                        self.close();
+                        self.setPlaceholder();
+                        self.nextSearchTerm = self.opts.nextSearchTerm(selected, self.search.val());
+                    }
+                });
+            }
+        },
+
+        isPlaceholderOptionSelected: function() {
+            var placeholderOption;
+            if (this.getPlaceholder() === undefined) return false; // no placeholder specified so no option should be considered
+            return ((placeholderOption = this.getPlaceholderOption()) !== undefined && placeholderOption.prop("selected"))
+                || (this.opts.element.val() === "")
+                || (this.opts.element.val() === undefined)
+                || (this.opts.element.val() === null);
+        },
+
+        // single
+        prepareOpts: function () {
+            var opts = this.parent.prepareOpts.apply(this, arguments),
+                self=this;
+
+            if (opts.element.get(0).tagName.toLowerCase() === "select") {
+                // install the selection initializer
+                opts.initSelection = function (element, callback) {
+                    var selected = element.find("option").filter(function() { return this.selected && !this.disabled });
+                    // a single select box always has a value, no need to null check 'selected'
+                    callback(self.optionToData(selected));
+                };
+            } else if ("data" in opts) {
+                // install default initSelection when applied to hidden input and data is local
+                opts.initSelection = opts.initSelection || function (element, callback) {
+                    var id = element.val();
+                    //search in data by id, storing the actual matching item
+                    var match = null;
+                    opts.query({
+                        matcher: function(term, text, el){
+                            var is_match = equal(id, opts.id(el));
+                            if (is_match) {
+                                match = el;
+                            }
+                            return is_match;
+                        },
+                        callback: !$.isFunction(callback) ? $.noop : function() {
+                            callback(match);
+                        }
+                    });
+                };
+            }
+
+            return opts;
+        },
+
+        // single
+        getPlaceholder: function() {
+            // if a placeholder is specified on a single select without a valid placeholder option ignore it
+            if (this.select) {
+                if (this.getPlaceholderOption() === undefined) {
+                    return undefined;
+                }
+            }
+
+            return this.parent.getPlaceholder.apply(this, arguments);
+        },
+
+        // single
+        setPlaceholder: function () {
+            var placeholder = this.getPlaceholder();
+
+            if (this.isPlaceholderOptionSelected() && placeholder !== undefined) {
+
+                // check for a placeholder option if attached to a select
+                if (this.select && this.getPlaceholderOption() === undefined) return;
+
+                this.selection.find(".select2-chosen").html(this.opts.escapeMarkup(placeholder));
+
+                this.selection.addClass("select2-default");
+
+                this.container.removeClass("select2-allowclear");
+            }
+        },
+
+        // single
+        postprocessResults: function (data, initial, noHighlightUpdate) {
+            var selected = 0, self = this, showSearchInput = true;
+
+            // find the selected element in the result list
+
+            this.findHighlightableChoices().each2(function (i, elm) {
+                if (equal(self.id(elm.data("select2-data")), self.opts.element.val())) {
+                    selected = i;
+                    return false;
+                }
+            });
+
+            // and highlight it
+            if (noHighlightUpdate !== false) {
+                if (initial === true && selected >= 0) {
+                    this.highlight(selected);
+                } else {
+                    this.highlight(0);
+                }
+            }
+
+            // hide the search box if this is the first we got the results and there are enough of them for search
+
+            if (initial === true) {
+                var min = this.opts.minimumResultsForSearch;
+                if (min >= 0) {
+                    this.showSearch(countResults(data.results) >= min);
+                }
+            }
+        },
+
+        // single
+        showSearch: function(showSearchInput) {
+            if (this.showSearchInput === showSearchInput) return;
+
+            this.showSearchInput = showSearchInput;
+
+            this.dropdown.find(".select2-search").toggleClass("select2-search-hidden", !showSearchInput);
+            this.dropdown.find(".select2-search").toggleClass("select2-offscreen", !showSearchInput);
+            //add "select2-with-searchbox" to the container if search box is shown
+            $(this.dropdown, this.container).toggleClass("select2-with-searchbox", showSearchInput);
+        },
+
+        // single
+        onSelect: function (data, options) {
+
+            if (!this.triggerSelect(data)) { return; }
+
+            var old = this.opts.element.val(),
+                oldData = this.data();
+
+            this.opts.element.val(this.id(data));
+            this.updateSelection(data);
+
+            this.opts.element.trigger({ type: "select2-selected", val: this.id(data), choice: data });
+
+            this.nextSearchTerm = this.opts.nextSearchTerm(data, this.search.val());
+            this.close();
+
+            if ((!options || !options.noFocus) && this.opts.shouldFocusInput(this)) {
+                this.focusser.focus();
+            }
+
+            if (!equal(old, this.id(data))) {
+                this.triggerChange({ added: data, removed: oldData });
+            }
+        },
+
+        // single
+        updateSelection: function (data) {
+
+            var container=this.selection.find(".select2-chosen"), formatted, cssClass;
+
+            this.selection.data("select2-data", data);
+
+            container.empty();
+            if (data !== null) {
+                formatted=this.opts.formatSelection(data, container, this.opts.escapeMarkup);
+            }
+            if (formatted !== undefined) {
+                container.append(formatted);
+            }
+            cssClass=this.opts.formatSelectionCssClass(data, container);
+            if (cssClass !== undefined) {
+                container.addClass(cssClass);
+            }
+
+            this.selection.removeClass("select2-default");
+
+            if (this.opts.allowClear && this.getPlaceholder() !== undefined) {
+                this.container.addClass("select2-allowclear");
+            }
+        },
+
+        // single
+        val: function () {
+            var val,
+                triggerChange = false,
+                data = null,
+                self = this,
+                oldData = this.data();
+
+            if (arguments.length === 0) {
+                return this.opts.element.val();
+            }
+
+            val = arguments[0];
+
+            if (arguments.length > 1) {
+                triggerChange = arguments[1];
+            }
+
+            if (this.select) {
+                this.select
+                    .val(val)
+                    .find("option").filter(function() { return this.selected }).each2(function (i, elm) {
+                        data = self.optionToData(elm);
+                        return false;
+                    });
+                this.updateSelection(data);
+                this.setPlaceholder();
+                if (triggerChange) {
+                    this.triggerChange({added: data, removed:oldData});
+                }
+            } else {
+                // val is an id. !val is true for [undefined,null,'',0] - 0 is legal
+                if (!val && val !== 0) {
+                    this.clear(triggerChange);
+                    return;
+                }
+                if (this.opts.initSelection === undefined) {
+                    throw new Error("cannot call val() if initSelection() is not defined");
+                }
+                this.opts.element.val(val);
+                this.opts.initSelection(this.opts.element, function(data){
+                    self.opts.element.val(!data ? "" : self.id(data));
+                    self.updateSelection(data);
+                    self.setPlaceholder();
+                    if (triggerChange) {
+                        self.triggerChange({added: data, removed:oldData});
+                    }
+                });
+            }
+        },
+
+        // single
+        clearSearch: function () {
+            this.search.val("");
+            this.focusser.val("");
+        },
+
+        // single
+        data: function(value) {
+            var data,
+                triggerChange = false;
+
+            if (arguments.length === 0) {
+                data = this.selection.data("select2-data");
+                if (data == undefined) data = null;
+                return data;
+            } else {
+                if (arguments.length > 1) {
+                    triggerChange = arguments[1];
+                }
+                if (!value) {
+                    this.clear(triggerChange);
+                } else {
+                    data = this.data();
+                    this.opts.element.val(!value ? "" : this.id(value));
+                    this.updateSelection(value);
+                    if (triggerChange) {
+                        this.triggerChange({added: value, removed:data});
+                    }
+                }
+            }
+        }
+    });
+
+    MultiSelect2 = clazz(AbstractSelect2, {
+
+        // multi
+        createContainer: function () {
+            var container = $(document.createElement("div")).attr({
+                "class": "select2-container select2-container-multi"
+            }).html([
+                "<ul class='select2-choices'>",
+                "  <li class='select2-search-field'>",
+                "    <label for='' class='select2-offscreen'></label>",
+                "    <input type='text' autocomplete='off' autocorrect='off' autocapitalize='off' spellcheck='false' class='select2-input'>",
+                "  </li>",
+                "</ul>",
+                "<div class='select2-drop select2-drop-multi select2-display-none'>",
+                "   <ul class='select2-results'>",
+                "   </ul>",
+                "</div>"].join(""));
+            return container;
+        },
+
+        // multi
+        prepareOpts: function () {
+            var opts = this.parent.prepareOpts.apply(this, arguments),
+                self=this;
+
+            // TODO validate placeholder is a string if specified
+            if (opts.element.get(0).tagName.toLowerCase() === "select") {
+                // install the selection initializer
+                opts.initSelection = function (element, callback) {
+
+                    var data = [];
+
+                    element.find("option").filter(function() { return this.selected && !this.disabled }).each2(function (i, elm) {
+                        data.push(self.optionToData(elm));
+                    });
+                    callback(data);
+                };
+            } else if ("data" in opts) {
+                // install default initSelection when applied to hidden input and data is local
+                opts.initSelection = opts.initSelection || function (element, callback) {
+                    var ids = splitVal(element.val(), opts.separator, opts.transformVal);
+                    //search in data by array of ids, storing matching items in a list
+                    var matches = [];
+                    opts.query({
+                        matcher: function(term, text, el){
+                            var is_match = $.grep(ids, function(id) {
+                                return equal(id, opts.id(el));
+                            }).length;
+                            if (is_match) {
+                                matches.push(el);
+                            }
+                            return is_match;
+                        },
+                        callback: !$.isFunction(callback) ? $.noop : function() {
+                            // reorder matches based on the order they appear in the ids array because right now
+                            // they are in the order in which they appear in data array
+                            var ordered = [];
+                            for (var i = 0; i < ids.length; i++) {
+                                var id = ids[i];
+                                for (var j = 0; j < matches.length; j++) {
+                                    var match = matches[j];
+                                    if (equal(id, opts.id(match))) {
+                                        ordered.push(match);
+                                        matches.splice(j, 1);
+                                        break;
+                                    }
+                                }
+                            }
+                            callback(ordered);
+                        }
+                    });
+                };
+            }
+
+            return opts;
+        },
+
+        // multi
+        selectChoice: function (choice) {
+
+            var selected = this.container.find(".select2-search-choice-focus");
+            if (selected.length && choice && choice[0] == selected[0]) {
+
+            } else {
+                if (selected.length) {
+                    this.opts.element.trigger("choice-deselected", selected);
+                }
+                selected.removeClass("select2-search-choice-focus");
+                if (choice && choice.length) {
+                    this.close();
+                    choice.addClass("select2-search-choice-focus");
+                    this.opts.element.trigger("choice-selected", choice);
+                }
+            }
+        },
+
+        // multi
+        destroy: function() {
+            $("label[for='" + this.search.attr('id') + "']")
+                .attr('for', this.opts.element.attr("id"));
+            this.parent.destroy.apply(this, arguments);
+
+            cleanupJQueryElements.call(this,
+                "searchContainer",
+                "selection"
+            );
+        },
+
+        // multi
+        initContainer: function () {
+
+            var selector = ".select2-choices", selection;
+
+            this.searchContainer = this.container.find(".select2-search-field");
+            this.selection = selection = this.container.find(selector);
+
+            var _this = this;
+            this.selection.on("click", ".select2-container:not(.select2-container-disabled) .select2-search-choice:not(.select2-locked)", function (e) {
+                _this.search[0].focus();
+                _this.selectChoice($(this));
+            });
+
+            // rewrite labels from original element to focusser
+            this.search.attr("id", "s2id_autogen"+nextUid());
+
+            this.search.prev()
+                .text($("label[for='" + this.opts.element.attr("id") + "']").text())
+                .attr('for', this.search.attr('id'));
+            this.opts.element.focus(this.bind(function () { this.focus(); }));
+
+            this.search.on("input paste", this.bind(function() {
+                if (this.search.attr('placeholder') && this.search.val().length == 0) return;
+                if (!this.isInterfaceEnabled()) return;
+                if (!this.opened()) {
+                    this.open();
+                }
+            }));
+
+            this.search.attr("tabindex", this.elementTabIndex);
+
+            this.keydowns = 0;
+            this.search.on("keydown", this.bind(function (e) {
+                if (!this.isInterfaceEnabled()) return;
+
+                ++this.keydowns;
+                var selected = selection.find(".select2-search-choice-focus");
+                var prev = selected.prev(".select2-search-choice:not(.select2-locked)");
+                var next = selected.next(".select2-search-choice:not(.select2-locked)");
+                var pos = getCursorInfo(this.search);
+
+                if (selected.length &&
+                    (e.which == KEY.LEFT || e.which == KEY.RIGHT || e.which == KEY.BACKSPACE || e.which == KEY.DELETE || e.which == KEY.ENTER)) {
+                    var selectedChoice = selected;
+                    if (e.which == KEY.LEFT && prev.length) {
+                        selectedChoice = prev;
+                    }
+                    else if (e.which == KEY.RIGHT) {
+                        selectedChoice = next.length ? next : null;
+                    }
+                    else if (e.which === KEY.BACKSPACE) {
+                        if (this.unselect(selected.first())) {
+                            this.search.width(10);
+                            selectedChoice = prev.length ? prev : next;
+                        }
+                    } else if (e.which == KEY.DELETE) {
+                        if (this.unselect(selected.first())) {
+                            this.search.width(10);
+                            selectedChoice = next.length ? next : null;
+                        }
+                    } else if (e.which == KEY.ENTER) {
+                        selectedChoice = null;
+                    }
+
+                    this.selectChoice(selectedChoice);
+                    killEvent(e);
+                    if (!selectedChoice || !selectedChoice.length) {
+                        this.open();
+                    }
+                    return;
+                } else if (((e.which === KEY.BACKSPACE && this.keydowns == 1)
+                    || e.which == KEY.LEFT) && (pos.offset == 0 && !pos.length)) {
+
+                    this.selectChoice(selection.find(".select2-search-choice:not(.select2-locked)").last());
+                    killEvent(e);
+                    return;
+                } else {
+                    this.selectChoice(null);
+                }
+
+                if (this.opened()) {
+                    switch (e.which) {
+                    case KEY.UP:
+                    case KEY.DOWN:
+                        this.moveHighlight((e.which === KEY.UP) ? -1 : 1);
+                        killEvent(e);
+                        return;
+                    case KEY.ENTER:
+                        this.selectHighlighted();
+                        killEvent(e);
+                        return;
+                    case KEY.TAB:
+                        this.selectHighlighted({noFocus:true});
+                        this.close();
+                        return;
+                    case KEY.ESC:
+                        this.cancel(e);
+                        killEvent(e);
+                        return;
+                    }
+                }
+
+                if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e)
+                 || e.which === KEY.BACKSPACE || e.which === KEY.ESC) {
+                    return;
+                }
+
+                if (e.which === KEY.ENTER) {
+                    if (this.opts.openOnEnter === false) {
+                        return;
+                    } else if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) {
+                        return;
+                    }
+                }
+
+                this.open();
+
+                if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) {
+                    // prevent the page from scrolling
+                    killEvent(e);
+                }
+
+                if (e.which === KEY.ENTER) {
+                    // prevent form from being submitted
+                    killEvent(e);
+                }
+
+            }));
+
+            this.search.on("keyup", this.bind(function (e) {
+                this.keydowns = 0;
+                this.resizeSearch();
+            })
+            );
+
+            this.search.on("blur", this.bind(function(e) {
+                this.container.removeClass("select2-container-active");
+                this.search.removeClass("select2-focused");
+                this.selectChoice(null);
+                if (!this.opened()) this.clearSearch();
+                e.stopImmediatePropagation();
+                this.opts.element.trigger($.Event("select2-blur"));
+            }));
+
+            this.container.on("click", selector, this.bind(function (e) {
+                if (!this.isInterfaceEnabled()) return;
+                if ($(e.target).closest(".select2-search-choice").length > 0) {
+                    // clicked inside a select2 search choice, do not open
+                    return;
+                }
+                this.selectChoice(null);
+                this.clearPlaceholder();
+                if (!this.container.hasClass("select2-container-active")) {
+                    this.opts.element.trigger($.Event("select2-focus"));
+                }
+                this.open();
+                this.focusSearch();
+                e.preventDefault();
+            }));
+
+            this.container.on("focus", selector, this.bind(function () {
+                if (!this.isInterfaceEnabled()) return;
+                if (!this.container.hasClass("select2-container-active")) {
+                    this.opts.element.trigger($.Event("select2-focus"));
+                }
+                this.container.addClass("select2-container-active");
+                this.dropdown.addClass("select2-drop-active");
+                this.clearPlaceholder();
+            }));
+
+            this.initContainerWidth();
+            this.opts.element.hide();
+
+            // set the placeholder if necessary
+            this.clearSearch();
+        },
+
+        // multi
+        enableInterface: function() {
+            if (this.parent.enableInterface.apply(this, arguments)) {
+                this.search.prop("disabled", !this.isInterfaceEnabled());
+            }
+        },
+
+        // multi
+        initSelection: function () {
+            var data;
+            if (this.opts.element.val() === "" && this.opts.element.text() === "") {
+                this.updateSelection([]);
+                this.close();
+                // set the placeholder if necessary
+                this.clearSearch();
+            }
+            if (this.select || this.opts.element.val() !== "") {
+                var self = this;
+                this.opts.initSelection.call(null, this.opts.element, function(data){
+                    if (data !== undefined && data !== null) {
+                        self.updateSelection(data);
+                        self.close();
+                        // set the placeholder if necessary
+                        self.clearSearch();
+                    }
+                });
+            }
+        },
+
+        // multi
+        clearSearch: function () {
+            var placeholder = this.getPlaceholder(),
+                maxWidth = this.getMaxSearchWidth();
+
+            if (placeholder !== undefined  && this.getVal().length === 0 && this.search.hasClass("select2-focused") === false) {
+                this.search.val(placeholder).addClass("select2-default");
+                // stretch the search box to full width of the container so as much of the placeholder is visible as possible
+                // we could call this.resizeSearch(), but we do not because that requires a sizer and we do not want to create one so early because of a firefox bug, see #944
+                this.search.width(maxWidth > 0 ? maxWidth : this.container.css("width"));
+            } else {
+                this.search.val("").width(10);
+            }
+        },
+
+        // multi
+        clearPlaceholder: function () {
+            if (this.search.hasClass("select2-default")) {
+                this.search.val("").removeClass("select2-default");
+            }
+        },
+
+        // multi
+        opening: function () {
+            this.clearPlaceholder(); // should be done before super so placeholder is not used to search
+            this.resizeSearch();
+
+            this.parent.opening.apply(this, arguments);
+
+            this.focusSearch();
+
+            // initializes search's value with nextSearchTerm (if defined by user)
+            // ignore nextSearchTerm if the dropdown is opened by the user pressing a letter
+            if(this.search.val() === "") {
+                if(this.nextSearchTerm != undefined){
+                    this.search.val(this.nextSearchTerm);
+                    this.search.select();
+                }
+            }
+
+            this.updateResults(true);
+            if (this.opts.shouldFocusInput(this)) {
+                this.search.focus();
+            }
+            this.opts.element.trigger($.Event("select2-open"));
+        },
+
+        // multi
+        close: function () {
+            if (!this.opened()) return;
+            this.parent.close.apply(this, arguments);
+        },
+
+        // multi
+        focus: function () {
+            this.close();
+            this.search.focus();
+        },
+
+        // multi
+        isFocused: function () {
+            return this.search.hasClass("select2-focused");
+        },
+
+        // multi
+        updateSelection: function (data) {
+            var ids = [], filtered = [], self = this;
+
+            // filter out duplicates
+            $(data).each(function () {
+                if (indexOf(self.id(this), ids) < 0) {
+                    ids.push(self.id(this));
+                    filtered.push(this);
+                }
+            });
+            data = filtered;
+
+            this.selection.find(".select2-search-choice").remove();
+            $(data).each(function () {
+                self.addSelectedChoice(this);
+            });
+            self.postprocessResults();
+        },
+
+        // multi
+        tokenize: function() {
+            var input = this.search.val();
+            input = this.opts.tokenizer.call(this, input, this.data(), this.bind(this.onSelect), this.opts);
+            if (input != null && input != undefined) {
+                this.search.val(input);
+                if (input.length > 0) {
+                    this.open();
+                }
+            }
+
+        },
+
+        // multi
+        onSelect: function (data, options) {
+
+            if (!this.triggerSelect(data) || data.text === "") { return; }
+
+            this.addSelectedChoice(data);
+
+            this.opts.element.trigger({ type: "selected", val: this.id(data), choice: data });
+
+            // keep track of the search's value before it gets cleared
+            this.nextSearchTerm = this.opts.nextSearchTerm(data, this.search.val());
+
+            this.clearSearch();
+            this.updateResults();
+
+            if (this.select || !this.opts.closeOnSelect) this.postprocessResults(data, false, this.opts.closeOnSelect===true);
+
+            if (this.opts.closeOnSelect) {
+                this.close();
+                this.search.width(10);
+            } else {
+                if (this.countSelectableResults()>0) {
+                    this.search.width(10);
+                    this.resizeSearch();
+                    if (this.getMaximumSelectionSize() > 0 && this.val().length >= this.getMaximumSelectionSize()) {
+                        // if we reached max selection size repaint the results so choices
+                        // are replaced with the max selection reached message
+                        this.updateResults(true);
+                    } else {
+                        // initializes search's value with nextSearchTerm and update search result
+                        if(this.nextSearchTerm != undefined){
+                            this.search.val(this.nextSearchTerm);
+                            this.updateResults();
+                            this.search.select();
+                        }
+                    }
+                    this.positionDropdown();
+                } else {
+                    // if nothing left to select close
+                    this.close();
+                    this.search.width(10);
+                }
+            }
+
+            // since its not possible to select an element that has already been
+            // added we do not need to check if this is a new element before firing change
+            this.triggerChange({ added: data });
+
+            if (!options || !options.noFocus)
+                this.focusSearch();
+        },
+
+        // multi
+        cancel: function () {
+            this.close();
+            this.focusSearch();
+        },
+
+        addSelectedChoice: function (data) {
+            var enableChoice = !data.locked,
+                enabledItem = $(
+                    "<li class='select2-search-choice'>" +
+                    "    <div></div>" +
+                    "    <a href='#' class='select2-search-choice-close' tabindex='-1'></a>" +
+                    "</li>"),
+                disabledItem = $(
+                    "<li class='select2-search-choice select2-locked'>" +
+                    "<div></div>" +
+                    "</li>");
+            var choice = enableChoice ? enabledItem : disabledItem,
+                id = this.id(data),
+                val = this.getVal(),
+                formatted,
+                cssClass;
+
+            formatted=this.opts.formatSelection(data, choice.find("div"), this.opts.escapeMarkup);
+            if (formatted != undefined) {
+                choice.find("div").replaceWith($("<div></div>").html(formatted));
+            }
+            cssClass=this.opts.formatSelectionCssClass(data, choice.find("div"));
+            if (cssClass != undefined) {
+                choice.addClass(cssClass);
+            }
+
+            if(enableChoice){
+              choice.find(".select2-search-choice-close")
+                  .on("mousedown", killEvent)
+                  .on("click dblclick", this.bind(function (e) {
+                  if (!this.isInterfaceEnabled()) return;
+
+                  this.unselect($(e.target));
+                  this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus");
+                  killEvent(e);
+                  this.close();
+                  this.focusSearch();
+              })).on("focus", this.bind(function () {
+                  if (!this.isInterfaceEnabled()) return;
+                  this.container.addClass("select2-container-active");
+                  this.dropdown.addClass("select2-drop-active");
+              }));
+            }
+
+            choice.data("select2-data", data);
+            choice.insertBefore(this.searchContainer);
+
+            val.push(id);
+            this.setVal(val);
+        },
+
+        // multi
+        unselect: function (selected) {
+            var val = this.getVal(),
+                data,
+                index;
+            selected = selected.closest(".select2-search-choice");
+
+            if (selected.length === 0) {
+                throw "Invalid argument: " + selected + ". Must be .select2-search-choice";
+            }
+
+            data = selected.data("select2-data");
+
+            if (!data) {
+                // prevent a race condition when the 'x' is clicked really fast repeatedly the event can be queued
+                // and invoked on an element already removed
+                return;
+            }
+
+            var evt = $.Event("select2-removing");
+            evt.val = this.id(data);
+            evt.choice = data;
+            this.opts.element.trigger(evt);
+
+            if (evt.isDefaultPrevented()) {
+                return false;
+            }
+
+            while((index = indexOf(this.id(data), val)) >= 0) {
+                val.splice(index, 1);
+                this.setVal(val);
+                if (this.select) this.postprocessResults();
+            }
+
+            selected.remove();
+
+            this.opts.element.trigger({ type: "select2-removed", val: this.id(data), choice: data });
+            this.triggerChange({ removed: data });
+
+            return true;
+        },
+
+        // multi
+        postprocessResults: function (data, initial, noHighlightUpdate) {
+            var val = this.getVal(),
+                choices = this.results.find(".select2-result"),
+                compound = this.results.find(".select2-result-with-children"),
+                self = this;
+
+            choices.each2(function (i, choice) {
+                var id = self.id(choice.data("select2-data"));
+                if (indexOf(id, val) >= 0) {
+                    choice.addClass("select2-selected");
+                    // mark all children of the selected parent as selected
+                    choice.find(".select2-result-selectable").addClass("select2-selected");
+                }
+            });
+
+            compound.each2(function(i, choice) {
+                // hide an optgroup if it doesn't have any selectable children
+                if (!choice.is('.select2-result-selectable')
+                    && choice.find(".select2-result-selectable:not(.select2-selected)").length === 0) {
+                    choice.addClass("select2-selected");
+                }
+            });
+
+            if (this.highlight() == -1 && noHighlightUpdate !== false && this.opts.closeOnSelect === true){
+                self.highlight(0);
+            }
+
+            //If all results are chosen render formatNoMatches
+            if(!this.opts.createSearchChoice && !choices.filter('.select2-result:not(.select2-selected)').length > 0){
+                if(!data || data && !data.more && this.results.find(".select2-no-results").length === 0) {
+                    if (checkFormatter(self.opts.formatNoMatches, "formatNoMatches")) {
+                        this.results.append("<li class='select2-no-results'>" + evaluate(self.opts.formatNoMatches, self.opts.element, self.search.val()) + "</li>");
+                    }
+                }
+            }
+
+        },
+
+        // multi
+        getMaxSearchWidth: function() {
+            return this.selection.width() - getSideBorderPadding(this.search);
+        },
+
+        // multi
+        resizeSearch: function () {
+            var minimumWidth, left, maxWidth, containerLeft, searchWidth,
+                sideBorderPadding = getSideBorderPadding(this.search);
+
+            minimumWidth = measureTextWidth(this.search) + 10;
+
+            left = this.search.offset().left;
+
+            maxWidth = this.selection.width();
+            containerLeft = this.selection.offset().left;
+
+            searchWidth = maxWidth - (left - containerLeft) - sideBorderPadding;
+
+            if (searchWidth < minimumWidth) {
+                searchWidth = maxWidth - sideBorderPadding;
+            }
+
+            if (searchWidth < 40) {
+                searchWidth = maxWidth - sideBorderPadding;
+            }
+
+            if (searchWidth <= 0) {
+              searchWidth = minimumWidth;
+            }
+
+            this.search.width(Math.floor(searchWidth));
+        },
+
+        // multi
+        getVal: function () {
+            var val;
+            if (this.select) {
+                val = this.select.val();
+                return val === null ? [] : val;
+            } else {
+                val = this.opts.element.val();
+                return splitVal(val, this.opts.separator, this.opts.transformVal);
+            }
+        },
+
+        // multi
+        setVal: function (val) {
+            var unique;
+            if (this.select) {
+                this.select.val(val);
+            } else {
+                unique = [];
+                // filter out duplicates
+                $(val).each(function () {
+                    if (indexOf(this, unique) < 0) unique.push(this);
+                });
+                this.opts.element.val(unique.length === 0 ? "" : unique.join(this.opts.separator));
+            }
+        },
+
+        // multi
+        buildChangeDetails: function (old, current) {
+            var current = current.slice(0),
+                old = old.slice(0);
+
+            // remove intersection from each array
+            for (var i = 0; i < current.length; i++) {
+                for (var j = 0; j < old.length; j++) {
+                    if (equal(this.opts.id(current[i]), this.opts.id(old[j]))) {
+                        current.splice(i, 1);
+                        if(i>0){
+                            i--;
+                        }
+                        old.splice(j, 1);
+                        j--;
+                    }
+                }
+            }
+
+            return {added: current, removed: old};
+        },
+
+
+        // multi
+        val: function (val, triggerChange) {
+            var oldData, self=this;
+
+            if (arguments.length === 0) {
+                return this.getVal();
+            }
+
+            oldData=this.data();
+            if (!oldData.length) oldData=[];
+
+            // val is an id. !val is true for [undefined,null,'',0] - 0 is legal
+            if (!val && val !== 0) {
+                this.opts.element.val("");
+                this.updateSelection([]);
+                this.clearSearch();
+                if (triggerChange) {
+                    this.triggerChange({added: this.data(), removed: oldData});
+                }
+                return;
+            }
+
+            // val is a list of ids
+            this.setVal(val);
+
+            if (this.select) {
+                this.opts.initSelection(this.select, this.bind(this.updateSelection));
+                if (triggerChange) {
+                    this.triggerChange(this.buildChangeDetails(oldData, this.data()));
+                }
+            } else {
+                if (this.opts.initSelection === undefined) {
+                    throw new Error("val() cannot be called if initSelection() is not defined");
+                }
+
+                this.opts.initSelection(this.opts.element, function(data){
+                    var ids=$.map(data, self.id);
+                    self.setVal(ids);
+                    self.updateSelection(data);
+                    self.clearSearch();
+                    if (triggerChange) {
+                        self.triggerChange(self.buildChangeDetails(oldData, self.data()));
+                    }
+                });
+            }
+            this.clearSearch();
+        },
+
+        // multi
+        onSortStart: function() {
+            if (this.select) {
+                throw new Error("Sorting of elements is not supported when attached to <select>. Attach to <input type='hidden'/> instead.");
+            }
+
+            // collapse search field into 0 width so its container can be collapsed as well
+            this.search.width(0);
+            // hide the container
+            this.searchContainer.hide();
+        },
+
+        // multi
+        onSortEnd:function() {
+
+            var val=[], self=this;
+
+            // show search and move it to the end of the list
+            this.searchContainer.show();
+            // make sure the search container is the last item in the list
+            this.searchContainer.appendTo(this.searchContainer.parent());
+            // since we collapsed the width in dragStarted, we resize it here
+            this.resizeSearch();
+
+            // update selection
+            this.selection.find(".select2-search-choice").each(function() {
+                val.push(self.opts.id($(this).data("select2-data")));
+            });
+            this.setVal(val);
+            this.triggerChange();
+        },
+
+        // multi
+        data: function(values, triggerChange) {
+            var self=this, ids, old;
+            if (arguments.length === 0) {
+                 return this.selection
+                     .children(".select2-search-choice")
+                     .map(function() { return $(this).data("select2-data"); })
+                     .get();
+            } else {
+                old = this.data();
+                if (!values) { values = []; }
+                ids = $.map(values, function(e) { return self.opts.id(e); });
+                this.setVal(ids);
+                this.updateSelection(values);
+                this.clearSearch();
+                if (triggerChange) {
+                    this.triggerChange(this.buildChangeDetails(old, this.data()));
+                }
+            }
+        }
+    });
+
+    $.fn.select2 = function () {
+
+        var args = Array.prototype.slice.call(arguments, 0),
+            opts,
+            select2,
+            method, value, multiple,
+            allowedMethods = ["val", "destroy", "opened", "open", "close", "focus", "isFocused", "container", "dropdown", "onSortStart", "onSortEnd", "enable", "disable", "readonly", "positionDropdown", "data", "search"],
+            valueMethods = ["opened", "isFocused", "container", "dropdown"],
+            propertyMethods = ["val", "data"],
+            methodsMap = { search: "externalSearch" };
+
+        this.each(function () {
+            if (args.length === 0 || typeof(args[0]) === "object") {
+                opts = args.length === 0 ? {} : $.extend({}, args[0]);
+                opts.element = $(this);
+
+                if (opts.element.get(0).tagName.toLowerCase() === "select") {
+                    multiple = opts.element.prop("multiple");
+                } else {
+                    multiple = opts.multiple || false;
+                    if ("tags" in opts) {opts.multiple = multiple = true;}
+                }
+
+                select2 = multiple ? new window.Select2["class"].multi() : new window.Select2["class"].single();
+                select2.init(opts);
+            } else if (typeof(args[0]) === "string") {
+
+                if (indexOf(args[0], allowedMethods) < 0) {
+                    throw "Unknown method: " + args[0];
+                }
+
+                value = undefined;
+                select2 = $(this).data("select2");
+                if (select2 === undefined) return;
+
+                method=args[0];
+
+                if (method === "container") {
+                    value = select2.container;
+                } else if (method === "dropdown") {
+                    value = select2.dropdown;
+                } else {
+                    if (methodsMap[method]) method = methodsMap[method];
+
+                    value = select2[method].apply(select2, args.slice(1));
+                }
+                if (indexOf(args[0], valueMethods) >= 0
+                    || (indexOf(args[0], propertyMethods) >= 0 && args.length == 1)) {
+                    return false; // abort the iteration, ready to return first matched value
+                }
+            } else {
+                throw "Invalid arguments to select2 plugin: " + args;
+            }
+        });
+        return (value === undefined) ? this : value;
+    };
+
+    // plugin defaults, accessible to users
+    $.fn.select2.defaults = {
+        width: "copy",
+        loadMorePadding: 0,
+        closeOnSelect: true,
+        openOnEnter: true,
+        containerCss: {},
+        dropdownCss: {},
+        containerCssClass: "",
+        dropdownCssClass: "",
+        formatResult: function(result, container, query, escapeMarkup) {
+            var markup=[];
+            markMatch(this.text(result), query.term, markup, escapeMarkup);
+            return markup.join("");
+        },
+        transformVal: function(val) {
+            return $.trim(val);
+        },
+        formatSelection: function (data, container, escapeMarkup) {
+            return data ? escapeMarkup(this.text(data)) : undefined;
+        },
+        sortResults: function (results, container, query) {
+            return results;
+        },
+        formatResultCssClass: function(data) {return data.css;},
+        formatSelectionCssClass: function(data, container) {return undefined;},
+        minimumResultsForSearch: 0,
+        minimumInputLength: 0,
+        maximumInputLength: null,
+        maximumSelectionSize: 0,
+        id: function (e) { return e == undefined ? null : e.id; },
+        text: function (e) {
+          if (e && this.data && this.data.text) {
+            if ($.isFunction(this.data.text)) {
+              return this.data.text(e);
+            } else {
+              return e[this.data.text];
+            }
+          } else {
+            return e.text;
+          }
+        },
+        matcher: function(term, text) {
+            return stripDiacritics(''+text).toUpperCase().indexOf(stripDiacritics(''+term).toUpperCase()) >= 0;
+        },
+        separator: ",",
+        tokenSeparators: [],
+        tokenizer: defaultTokenizer,
+        escapeMarkup: defaultEscapeMarkup,
+        blurOnChange: false,
+        selectOnBlur: false,
+        adaptContainerCssClass: function(c) { return c; },
+        adaptDropdownCssClass: function(c) { return null; },
+        nextSearchTerm: function(selectedObject, currentSearchTerm) { return undefined; },
+        searchInputPlaceholder: '',
+        createSearchChoicePosition: 'top',
+        shouldFocusInput: function (instance) {
+            // Attempt to detect touch devices
+            var supportsTouchEvents = (('ontouchstart' in window) ||
+                                       (navigator.msMaxTouchPoints > 0));
+
+            // Only devices which support touch events should be special cased
+            if (!supportsTouchEvents) {
+                return true;
+            }
+
+            // Never focus the input if search is disabled
+            if (instance.opts.minimumResultsForSearch < 0) {
+                return false;
+            }
+
+            return true;
+        }
+    };
+
+    $.fn.select2.locales = [];
+
+    $.fn.select2.locales['en'] = {
+         formatMatches: function (matches) { if (matches === 1) { return "One result is available, press enter to select it."; } return matches + " results are available, use up and down arrow keys to navigate."; },
+         formatNoMatches: function () { return "No matches found"; },
+         formatAjaxError: function (jqXHR, textStatus, errorThrown) { return "Loading failed"; },
+         formatInputTooShort: function (input, min) { var n = min - input.length; return "Please enter " + n + " or more character" + (n == 1 ? "" : "s"); },
+         formatInputTooLong: function (input, max) { var n = input.length - max; return "Please delete " + n + " character" + (n == 1 ? "" : "s"); },
+         formatSelectionTooBig: function (limit) { return "You can only select " + limit + " item" + (limit == 1 ? "" : "s"); },
+         formatLoadMore: function (pageNumber) { return "Loading more results…"; },
+         formatSearching: function () { return "Searching…"; }
+    };
+
+    $.extend($.fn.select2.defaults, $.fn.select2.locales['en']);
+
+    $.fn.select2.ajaxDefaults = {
+        transport: $.ajax,
+        params: {
+            type: "GET",
+            cache: false,
+            dataType: "json"
+        }
+    };
+
+    // exports
+    window.Select2 = {
+        query: {
+            ajax: ajax,
+            local: local,
+            tags: tags
+        }, util: {
+            debounce: debounce,
+            markMatch: markMatch,
+            escapeMarkup: defaultEscapeMarkup,
+            stripDiacritics: stripDiacritics
+        }, "class": {
+            "abstract": AbstractSelect2,
+            "single": SingleSelect2,
+            "multi": MultiSelect2
+        }
+    };
+
+}(jQuery));
diff --git a/~dev_rating/media/js/sign.js b/~dev_rating/media/js/sign.js
index 14b699137d91da2571e72419b6a8aa011a51181e..ffac11fdaa53684e07495f937bca90baef4b8c94 100644
--- a/~dev_rating/media/js/sign.js
+++ b/~dev_rating/media/js/sign.js
@@ -9,7 +9,7 @@ $(function()
             if(data.success === true)
             {
                 $('#signin_b').val('Всё отлично!');
-                $('#signin_f').submit();
+                window.location.replace(URLdir);
             }
             else
             {
@@ -89,7 +89,7 @@ $(function()
             if(data.success === true)
             {
                 $('#signup_b').val('Всё отлично!');
-                $('#signup_f').submit();
+                window.location.replace(URLdir);
             }
             else
             {
diff --git a/~dev_rating/modules/account/classes/Kohana/Account.php b/~dev_rating/modules/account/classes/Kohana/Account.php
index 845111b456fb7685abb1a92fbb3a23489838b3da..5667090c7f4a4141a6dc99df04ea7dbbca9dd19f 100644
--- a/~dev_rating/modules/account/classes/Kohana/Account.php
+++ b/~dev_rating/modules/account/classes/Kohana/Account.php
@@ -42,72 +42,82 @@ class Kohana_Account {
     public function createTeacher($lastName, $firstName, $secondName, $degreeID, $departamentID)
     {
         $activationCode = $this->generateActivationCode();
-        $response = $this->_model->createTeacher($lastName, $firstName, $secondName, $degreeID, $departamentID, $activationCode);
-        if($response == -1)
-            return -1;
-        else
-            return $activationCode;
+        $response = $this->_model->
+                    createTeacher(  $lastName, $firstName, 
+                                    $secondName, $degreeID, 
+                                    $departamentID, $activationCode);
+        if ($response === -1) {
+            $activationCode = -1;
+        }
+        return $activationCode;
     }
     
     public function createTeacherByDepName($lastName, $firstName, $secondName, $departamentName)
     {
         $activationCode = $this->generateActivationCode();
-        $response = $this->_model->createTeacherByDepName($lastName, $firstName, $secondName, $departamentName, $activationCode);
-        if($response == -1)
-            return -1;
-        else
-            return $activationCode;
+        $response = $this->_model->
+                    createTeacherByDepName( $lastName, $firstName, 
+                                            $secondName, $departamentName, 
+                                            $activationCode);
+        if ($response === -1) {
+            $activationCode = -1;
+        }
+        return $activationCode;
     }
     
-    public function createStudent($lastName, $firstName, $secondName, $grade, $groupNum, $facultyID)
+    public function createStudent(  $lastName, $firstName, $secondName, 
+                                    $grade, $groupNum, $facultyID)
     {
         $activationCode = $this->generateActivationCode();
-        $response = $this->_model->createStudent($lastName, $firstName, $secondName, $grade, $groupNum, $facultyID, $activationCode);
-        if($response == -1)
-            return -1;
-        else
-            return $activationCode;
+        $response = $this->_model->
+                    createStudent(  $lastName, $firstName, $secondName, 
+                                    $grade, $groupNum, $facultyID, 
+                                    $activationCode);
+        if ($response === -1) {
+            $activationCode = -1;
+        }
+        return $activationCode;
     }
 	
     public function createStudentEx($lastName, $firstName, $secondName, $studentGradeNum, $studentGroupNum, $studentDegree, $studentSpec, $facultyID)
     {
         $activationCode = $this->generateActivationCode();
         $response = $this->_model->createStudentEx($lastName, $firstName, $secondName, $studentGradeNum, $studentGroupNum, $studentDegree, $studentSpec, $facultyID, $activationCode);
-        if($response == -1)
-            return -1;
-        else
-            return $activationCode;
+        if ($response === -1) {
+            $activationCode = -1;
+        }
+        return $activationCode;
     }
 
     public function createSubject($name, $abbr, $facultyID)
     {
-        $response = $this->_model->createSubject($name, $abbr, $facultyID);
-        if($response == -1)
-            return -1;
-        else
-            return $activationCode;
+        $response = $this->_model->
+                    createSubject($name, $abbr, $facultyID);
+        if ($response === -1) {
+            $activationCode = -1;
+        }
+        return $activationCode;
     }
     
     private function checkTokenLifetime($creationDate)
     {
         $config = Kohana::$config->load('security.securityPolicy');
-        return (time() - $creationDate) > $config['recoveryToken']['lifetime'];
+        $lifetime = $config['recoveryToken']['lifetime'];
+        return (time() - $creationDate) > $lifetime;
     }
 
 
     private function checkRecoveryStatus($email)
     {
         $recovery = $this->_model->GetRecoveryInfoByEMail($email);
-        $recoveryHandled = array();
         $status = true;
-        foreach($recovery as $row)
-        {
-            if($this->checkTokenLifetime(strtotime($row['Date'])))
-            {
+        foreach($recovery as $row) {
+            $date = strtotime($row['Date']);
+            if($this->checkTokenLifetime($date)) {
                 $this->_model->useRecoveryToken($row['Token']);
-            }
-            else
+            } else {
                 $status = false;
+            }
         }
         return $status;
     }
@@ -116,14 +126,18 @@ class Kohana_Account {
     {
         $config = Kohana::$config->load('security.securityPolicy');
         $recovery = $this->_model->getRecoveryInfoByToken($token)->offsetGet(0);
-        if($recovery['isUsed'])
-            return false;
-        if($this->checkTokenLifetime(strtotime($recovery['Date'])))
-        {
-            $this->_model->useRecoveryToken($recovery['Token']);
-            return false;
+        $response = true;
+        
+        if ($recovery['isUsed']) {
+            $response = false;
+        } else {
+            $date = strtotime($recovery['Date']);
+            if($this->checkTokenLifetime($date)) {
+                $this->_model->useRecoveryToken($recovery['Token']);
+                $response = false;
+            }
         }
-        return true;
+        return $response;
     }
     
     public function createRecoveryRequest($email)
@@ -132,12 +146,14 @@ class Kohana_Account {
         if($this->checkRecoveryStatus($email))
         {
             $this->_model->createRecoveryToken($email, $requestToken);
-            $subject =  ASSEMBLY_SYSTEM_NAME.": Восстановление пароля"; 
+            $subject =  ASSEMBLY_SYSTEM_NAME.": Восстановление пароля";
+            
             $twig = Twig::factory('email/recovery');
             $twig->curl = URL::base(TRUE, 'https');
             $twig->Token = $requestToken;
             $twig->EMail = $email;
             $twig->Subject = $subject;
+            
             $message = $twig->render();
             $headers = 'MIME-Version: 1.0' . "\r\n";
             $headers .= 'Content-type: text/html; charset=utf-8' . "\r\n";
@@ -183,6 +199,4 @@ class Kohana_Account {
         $response = $this->_model->changePassword($id, $newPassword);
         return $response != -1;
     }
-        
-    
 }
\ No newline at end of file
diff --git a/~dev_rating/modules/account/classes/Kohana/User.php b/~dev_rating/modules/account/classes/Kohana/User.php
index 135562dc6120eb9e7a4e4857aa687aebdb6c1ff7..90bc1fba127723ddc527871575ac40e2e82ab8da 100644
--- a/~dev_rating/modules/account/classes/Kohana/User.php
+++ b/~dev_rating/modules/account/classes/Kohana/User.php
@@ -7,15 +7,15 @@ class Kohana_User implements ArrayAccess {
     protected $_config;
     protected $_model;
     protected $_userInfo;
-
-    const SESSION_LIFETIME = 1800; //seconds
+    protected static $_flag;
 
     /**
      * Вовзращает экземпляр класса (singleton-паттерн)
      *
      * @return  instance
      */     
-    public static function instance() {
+    public static function instance($state = false) {
+        self::$_flag = $state;
         if(!isset(self::$_instance))
         {
             $config = Kohana::$config->load('account');
@@ -24,6 +24,7 @@ class Kohana_User implements ArrayAccess {
         return self::$_instance;
     }
     
+    
     private function __construct($config = array()) {
         $this->_config = $config;
         $this->_session = Session::instance();
@@ -32,31 +33,17 @@ class Kohana_User implements ArrayAccess {
         $this->_config['hash_key'] = $this->_model->getHashKey();
         $this->_config['hash_method'] = 'sha256';
         $isSignedIn = $this->isSignedIn();
-        if($isSignedIn)
-        {
+        if($isSignedIn) {
             $id = $this->_session->get('ID');
-            $this->_userInfo = $this->_getInfoFromDB($id);
-        }
-
-
-        if ($isSignedIn) {
-            $last_time = $this->_session->get('last_time');
-            $cur_time = time();
-            $timeout = self::SESSION_LIFETIME;
-            if (isset($last_time) AND $last_time != null) {
-                $dif_time = $cur_time - $last_time;
-                if ($dif_time > $timeout) {
-                    $this->completeSignOut();
-                }
-                $this->_session->set('dif_time', $dif_time);
-            } else {
-                $this->_session->set('dif_time', $timeout+10); 
+            $this->_userInfo = $this->_getInfoFromDB($id); 
+            if (self::$_flag != true) {
+                $this->_session->regenerate();
+                $this->_session->set('start_time', time());
             }
-            $this->_session->set('last_time', $cur_time);    
         }
-
     }
     
+
     /**
      * Регистрирует нового пользователя и осуществляет вход.
      * Проверяет корректность кода активации и существование аккаунтов с такими же авторизационными данными.
@@ -69,28 +56,26 @@ class Kohana_User implements ArrayAccess {
      */          
     public function signUp($code, $email, $login, $password)
     {
-        if($this->_model->isActivationCodeValid($code))
-        {
-        	$isLogin = Account::instance()->isLoginExists($login);
-        	$isMail = Account::instance()->isMailExists($email);
-            if(!$isLogin && !$isMail)
-            {
-                $id = $this->_model->activateAccount($login, $password, $email, $code);
-                $this->completeSignIn($id, $this->hash($password));
-                return array(true, 'ok');
-            }
-            else
-            {
-                if($isLogin)
-                    return array(false, 'login_exists');
-                if($isMail)
-                    return array(false, 'mail_exists');
-            }
-        }
-        else
-        {
+        $model = &$this->_model;
+        $account = Account::instance();
+        
+        $isValid = $model->isActivationCodeValid($code);
+        if (!$isValid) {
             return array(false, 'invalid_code');
         }
+
+        $isLogin = $account->isLoginExists($login);
+        $isMail = $account->isMailExists($email);
+        
+        if ($isLogin) {
+            return array(false, 'login_exists');
+        } else if ($isMail) {
+            return array(false, 'mail_exists');
+        }
+        
+        $id = $model->activateAccount($login, $password, $email, $code);
+        $this->completeSignIn($id, $this->hash($password));
+        return array(true, 'ok');
     }
 
     /**
@@ -101,23 +86,25 @@ class Kohana_User implements ArrayAccess {
      * @return  bool
      */      
     public function signIn($login, $password) {
-        $id = $this->_model->checkAuth($login, $password);
-        if($id == -1)
+        $id = (int)$this->_model->checkAuth($login, $password);
+        if ($id === -1) {
             return false;
-        else
+        } else {
             return $this->completeSignIn($id, $this->hash($password));
+        }
     }
     
     protected function completeSignIn($id, $passhash) {
         $userHash = $this->hash($id.Request::$user_agent.Request::$client_ip).$this->_config['hash_key'];
-        $passhash = $this->hash($passhash.$this->_config['hash_key']);
-        Cookie::set('userhash', $passhash);
+        $passwordHash = $this->hash($passhash.$this->_config['hash_key']);
+        Cookie::set('userhash', $passwordHash);
         $this->_userInfo = $this->_getInfoFromDB($id);
         $this->_session->regenerate();
         $this->_session->set('ID', $id);
         $this->_session->set('LoggedIn', true);
         $this->_session->set('UserHash', $this->hash($userHash));
-        $this->_session->set('PasswordHash', $passhash);
+        $this->_session->set('PasswordHash', $passwordHash);
+        $this->_session->set('start_time', time());
         return TRUE;
     }    
 
@@ -128,13 +115,9 @@ class Kohana_User implements ArrayAccess {
      */   
     public function isSignedIn()
     {
-        if($this->_session->get('LoggedIn'))
-        {
-            if(!$this->checkHash())
-            {
-                $this->completeSignOut();
-                return false;
-            }
+        $session = &$this->_session;
+        if($session->get('LoggedIn') && !$this->checkHash()) {
+            $this->completeSignOut();
         }
         return $this->_session->get('LoggedIn');
     }
@@ -155,15 +138,17 @@ class Kohana_User implements ArrayAccess {
      */    
     public function signOut()
     {
-        if($this->isSignedIn())
-        {
+        if($this->isSignedIn()) {
             return $this->completeSignOut();
         }
+        return FALSE;
     }
     
     protected function completeSignOut()
     {
-        $this->_session->set('ID', FALSE)->set('LoggedIn', FALSE)->set('UserHash', FALSE);
+        $this->_session ->set('ID', FALSE)
+                        ->set('LoggedIn', FALSE)
+                        ->set('UserHash', FALSE);
         Cookie::delete('userhash');
         unset($this->_userInfo);
         $this->_session->restart();
@@ -262,12 +247,9 @@ class Kohana_User implements ArrayAccess {
     // ---------------------------- [ARRAY] ------------------------------------
     
     public function offsetSet($offset, $value) {
-        if(isset($this->_userInfo[$offset]))
-        {
+        if(isset($this->_userInfo[$offset])) {
             return $this->_userInfo[$offset];
-        }
-        else
-        {
+        } else {
             throw new Kohana_Exception('Invalid key: '.$offset);
         }
     }
diff --git a/~dev_rating/modules/account/classes/Model/Kohana/Account.php b/~dev_rating/modules/account/classes/Model/Kohana/Account.php
index c834275bc497c08ec0137d3ed90065cdc708ed6e..8c35309cbb00cdcb3b2d2b10d5f237573ddb4009 100644
--- a/~dev_rating/modules/account/classes/Model/Kohana/Account.php
+++ b/~dev_rating/modules/account/classes/Model/Kohana/Account.php
@@ -5,8 +5,8 @@ class Model_Kohana_Account extends Model
     public function setHashKey($key)
     {
         $sql = "SELECT `SetHashKey`('$key') AS `Key`;";
-        $key = DB::query(Database::SELECT, $sql)->execute();
-        return $key->get('Key');
+        $res = DB::query(Database::SELECT, $sql)->execute();
+        return $res->get('Key');
     }
     
     public function getHashKey()
@@ -18,8 +18,8 @@ class Model_Kohana_Account extends Model
     
     public function checkAuth($login, $password) {
         $sql = "SELECT `SignIn`('$login', '$password') AS `ID`;";
-        $login = DB::query(Database::SELECT, $sql)->execute();
-        return $login->get('ID');
+        $res = DB::query(Database::SELECT, $sql)->execute();
+        return $res->get('ID');
     }
     
     public function ChangeTeacherInfo($id, $lastName, $firstName, $secondName, $degreeID, $departamentID)
@@ -102,15 +102,15 @@ class Model_Kohana_Account extends Model
     public function getAccNumByLogin($login)
     {
         $sql = "SELECT `GetAccCountByLogin`('$login') AS Num;";
-        $login = DB::query(Database::SELECT, $sql)->execute();
-        return $login->get('Num');
+        $res = DB::query(Database::SELECT, $sql)->execute();
+        return $res->get('Num');
     }
     
     public function getAccNumByMail($email)
     {
         $sql = "SELECT `GetAccCountByMail`('$email') AS Num;";
-        $email = DB::query(Database::SELECT, $sql)->execute();
-        return $email->get('Num');
+        $res = DB::query(Database::SELECT, $sql)->execute();
+        return $res->get('Num');
     }    
     
     public function isActivationCodeValid($code)
@@ -126,8 +126,8 @@ class Model_Kohana_Account extends Model
     public function createRecoveryToken($email, $token)
     {
         $sql = "SELECT `CreateRecoveryToken`('$email', '$token') AS Num;";
-        $email = DB::query(Database::SELECT, $sql)->execute();
-        return $email->get('Num');
+        $res = DB::query(Database::SELECT, $sql)->execute();
+        return $res->get('Num');
     }
     
     public function getRecoveryInfoByEMail($email)