diff --git a/tutor/.gitignore b/tutor/.gitignore index 5830e151..0cf0cc9a 100644 --- a/tutor/.gitignore +++ b/tutor/.gitignore @@ -20,7 +20,6 @@ /public/uploads/* *.rbc capybara-*.html -.rspec /coverage/ /spec/tmp **.orig @@ -40,4 +39,5 @@ models.svg # Ignoring .java and .class files /students_solutions/Java/*.java -/students_solutions/Class/*.class \ No newline at end of file +/students_solutions/Class/*.class +/students_solutions/* diff --git a/tutor/Gemfile b/tutor/Gemfile index 09df078a..268ec354 100644 --- a/tutor/Gemfile +++ b/tutor/Gemfile @@ -62,6 +62,8 @@ gem 'foreman' # Use composite primary keys in models gem 'composite_primary_keys', '~> 6.0.1' +gem 'activeadmin', github: 'gregbell/active_admin' + # gem 'acts_as_list' @@ -72,6 +74,9 @@ gem 'json_builder', '3.1.0' # whenever gem for scheduling tasks gem 'whenever', :require => false +#pagination +gem 'will_paginate', '~> 3.0.5' + group :development, :test do gem 'rspec-rails', '~> 3.0.0.beta' end diff --git a/tutor/Gemfile.lock b/tutor/Gemfile.lock index e2362805..66c415e6 100644 --- a/tutor/Gemfile.lock +++ b/tutor/Gemfile.lock @@ -1,3 +1,20 @@ +GIT + remote: git://github.com/gregbell/active_admin.git + revision: 109ab88cb7c0777d3460cbfac3cc217068ecfabf + specs: + activeadmin (1.0.0.pre) + arbre (~> 1.0) + bourbon + coffee-rails + formtastic (~> 2.3.0.rc3) + inherited_resources (~> 1.3) + jquery-rails + jquery-ui-rails + kaminari (~> 0.15) + rails (>= 3.2, < 4.2) + ransack (~> 1.0) + sass-rails + GEM remote: https://rubygems.org/ specs: @@ -28,8 +45,13 @@ GEM acts_as_list (0.4.0) activerecord (>= 3.0) ansi (1.4.3) + arbre (1.0.1) + activesupport (>= 3.0.0) arel (4.0.2) bcrypt (3.1.7) + bourbon (3.2.1) + sass (~> 3.2) + thor builder (3.1.4) carrierwave (0.10.0) activemodel (>= 3.2.0) @@ -60,19 +82,35 @@ GEM foreman (0.63.0) dotenv (>= 0.7) thor (>= 0.13.6) +<<<<<<< HEAD googlecharts (1.6.8) +======= + formtastic (2.3.0.rc3) + actionpack (>= 3.0) + has_scope (0.6.0.rc) + actionpack (>= 3.2, < 5) + activesupport (>= 3.2, < 5) +>>>>>>> a5626aae62dca494a449268dc151db45acefa5e9 hashr (0.0.22) hike (1.2.3) i18n (0.6.9) + inherited_resources (1.4.1) + has_scope (~> 0.6.0.rc) + responders (~> 1.0.0.rc) jbuilder (1.5.3) activesupport (>= 3.0.0) multi_json (>= 1.2.0) jquery-rails (3.1.0) railties (>= 3.0, < 5.0) thor (>= 0.14, < 2.0) + jquery-ui-rails (4.2.1) + railties (>= 3.2.16) json (1.8.1) json_builder (3.1.0) activesupport (>= 2.0.0) + kaminari (0.15.1) + actionpack (>= 3.0.0) + activesupport (>= 3.0.0) kgio (2.9.2) mail (2.5.4) mime-types (~> 1.16) @@ -81,6 +119,8 @@ GEM minitest (4.7.5) multi_json (1.9.2) orm_adapter (0.5.0) + polyamorous (1.0.0) + activerecord (>= 3.0) polyglot (0.3.4) rack (1.5.2) rack-test (0.6.2) @@ -100,8 +140,16 @@ GEM thor (>= 0.18.1, < 2.0) raindrops (0.13.0) rake (10.3.1) + ransack (1.2.3) + actionpack (>= 3.0) + activerecord (>= 3.0) + activesupport (>= 3.0) + i18n + polyamorous (~> 1.0.0) rdoc (4.1.1) json (~> 1.4) + responders (1.0.0) + railties (>= 3.2, < 5) rest-client (1.6.7) mime-types (>= 1.16) rmagick (2.13.2) @@ -176,6 +224,7 @@ PLATFORMS ruby DEPENDENCIES + activeadmin! acts_as_list carrierwave chartkick diff --git a/tutor/app/admin/admin_user.rb b/tutor/app/admin/admin_user.rb new file mode 100644 index 00000000..433be5d7 --- /dev/null +++ b/tutor/app/admin/admin_user.rb @@ -0,0 +1,28 @@ +ActiveAdmin.register AdminUser do + permit_params :email, :password, :password_confirmation + + index do + selectable_column + id_column + column :email + column :current_sign_in_at + column :sign_in_count + column :created_at + actions + end + + filter :email + filter :current_sign_in_at + filter :sign_in_count + filter :created_at + + form do |f| + f.inputs "Admin Details" do + f.input :email + f.input :password + f.input :password_confirmation + end + f.actions + end + +end diff --git a/tutor/app/admin/dashboard.rb b/tutor/app/admin/dashboard.rb new file mode 100644 index 00000000..eae7baf0 --- /dev/null +++ b/tutor/app/admin/dashboard.rb @@ -0,0 +1,33 @@ +ActiveAdmin.register_page "Dashboard" do + + menu priority: 1, label: proc{ I18n.t("active_admin.dashboard") } + + content title: proc{ I18n.t("active_admin.dashboard") } do + div class: "blank_slate_container", id: "dashboard_default_message" do + span class: "blank_slate" do + span I18n.t("active_admin.dashboard_welcome.welcome") + small I18n.t("active_admin.dashboard_welcome.call_to_action") + end + end + + # Here is an example of a simple dashboard with columns and panels. + # + # columns do + # column do + # panel "Recent Posts" do + # ul do + # Post.recent(5).map do |post| + # li link_to(post.title, admin_post_path(post)) + # end + # end + # end + # end + + # column do + # panel "Info" do + # para "Welcome to ActiveAdmin." + # end + # end + # end + end # content +end diff --git a/tutor/app/assets/images/note.png b/tutor/app/assets/images/note.png new file mode 100644 index 00000000..453fad5c Binary files /dev/null and b/tutor/app/assets/images/note.png differ diff --git a/tutor/app/assets/images/tick.png b/tutor/app/assets/images/tick.png new file mode 100644 index 00000000..481b90a8 Binary files /dev/null and b/tutor/app/assets/images/tick.png differ diff --git a/tutor/app/assets/javascripts/_solutions.js b/tutor/app/assets/javascripts/_solutions.js new file mode 100644 index 00000000..1f174be2 --- /dev/null +++ b/tutor/app/assets/javascripts/_solutions.js @@ -0,0 +1,81 @@ +// [Editor cusomization - Story 3.19] +// Enable method "selectpicker" from the javascript file. +// Parameters: none. +// Returns: none. +// Author: Mimi +$(document).ready(function(e){ + $('.selectpicker').selectpicker(); +}); + +// [Editor cusomization - Story 3.19] +// change the theme of the editor. +// Parameters: +// The selected input from the 'theme' drop-downlist. +// Returns: none. +// Author: Mimi +function changeTheme(){ + var new_theme = $('#theme').val(); + editor.setTheme("ace/theme/"+new_theme); +} + +// [Editor cusomization - Story 3.19] +// change the mode of the editor. +// Parameters: +// The selected input from the 'mode' drop-downlist. +// Returns: none. +// Author: Mimi +function changeMode(){ + var new_mode = $('#mode').val(); + edit_session.setMode("ace/mode/"+new_mode); +} + +// [Editor cusomization - Story 3.19] +// change the font-size of the editor. +// Parameters: +// The selected input from the 'font' drop-downlist. +// Returns: none. +// Author: Mimi +function changeFont(){ + var new_font = $('#font').val(); + editor.setFontSize(parseInt(new_font)); +} + +// [Mark Solution - Story 4.29] +// shows the add note button when hovering over the the line +// Parameters: +// id: id of the div that was hovered over +// Returns: none +// Author: Abdullrahman Elhusseny +function show(id){ + id += "add_button" + document.getElementById(id).style.opacity = 1; +} + +// [Mark Solution - Story 4.29] +// hides the add note button when the crusor leaves the the line +// Parameters: +// id: id of the div that was hovered over +// Returns: none +// Author: Abdullrahman Elhusseny +function hide(id){ + id += "add_button" + document.getElementById(id).style.opacity = 0; +} + +// [Mark Solution - Story 4.29] +// pops a dialog box when trying to add or update note +// Parameters: +// id: id of the button that was pressed +// Returns: none +// Author: Abdullrahman Elhusseny +function pop(id){ + id += "form" + document.getElementById(id).style.display = 'initial' + $(document.getElementById(id)).bPopup({ + modalClose: false, + opacity: 0.6, + positionStyle: 'fixed', + speed: 650, + transition: 'slideIn' + }); +} \ No newline at end of file diff --git a/tutor/app/assets/javascripts/application.js b/tutor/app/assets/javascripts/application.js index ed3af524..dbe4160e 100644 --- a/tutor/app/assets/javascripts/application.js +++ b/tutor/app/assets/javascripts/application.js @@ -12,6 +12,19 @@ // //= require jquery //= require jquery_ujs +//= require jquery-ui-1.10.4 //= require_tree . +//= require bootstrap //= require jquery.tokeninput -//= require utilities \ No newline at end of file +//= require utilities + +// [Simple Search auto-complete - Story 1.23] +// autocomplete for the search bar +// Parameters: search term +// Returns: Array with the matched results +// Author: Ahmed Elassuty +$(function(){ + $('#search_field').autocomplete({ + source: $('#search_field').data("autocomplete-source") + }); +}); diff --git a/tutor/app/assets/javascripts/assignment_problems.js.coffee b/tutor/app/assets/javascripts/assignment_problems.js.coffee new file mode 100644 index 00000000..ff7fdf48 --- /dev/null +++ b/tutor/app/assets/javascripts/assignment_problems.js.coffee @@ -0,0 +1,3 @@ +@showDiv = -> + ocument.getElementById("test").style.display = "" + return \ No newline at end of file diff --git a/tutor/app/assets/javascripts/assignments.js.coffee b/tutor/app/assets/javascripts/assignments.js.coffee new file mode 100644 index 00000000..24f83d18 --- /dev/null +++ b/tutor/app/assets/javascripts/assignments.js.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/tutor/app/assets/javascripts/ace.js b/tutor/app/assets/javascripts/editor/ace.js similarity index 100% rename from tutor/app/assets/javascripts/ace.js rename to tutor/app/assets/javascripts/editor/ace.js diff --git a/tutor/app/assets/javascripts/mode-java.js b/tutor/app/assets/javascripts/editor/mode-java.js similarity index 100% rename from tutor/app/assets/javascripts/mode-java.js rename to tutor/app/assets/javascripts/editor/mode-java.js diff --git a/tutor/app/assets/javascripts/editor/mode-python.js b/tutor/app/assets/javascripts/editor/mode-python.js new file mode 100644 index 00000000..b2d02e1a --- /dev/null +++ b/tutor/app/assets/javascripts/editor/mode-python.js @@ -0,0 +1,296 @@ +// Source: Ace editor +// Integrated by: Mimi +/* ***** BEGIN LICENSE BLOCK ***** + * Distributed under the BSD license: + * + * Copyright (c) 2010, Ajax.org B.V. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Ajax.org B.V. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ***** END LICENSE BLOCK ***** */ + +ace.define('ace/mode/python', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/mode/text', 'ace/mode/python_highlight_rules', 'ace/mode/folding/pythonic', 'ace/range'], function(require, exports, module) { + + +var oop = require("../lib/oop"); +var TextMode = require("./text").Mode; +var PythonHighlightRules = require("./python_highlight_rules").PythonHighlightRules; +var PythonFoldMode = require("./folding/pythonic").FoldMode; +var Range = require("../range").Range; + +var Mode = function() { + this.HighlightRules = PythonHighlightRules; + this.foldingRules = new PythonFoldMode("\\:"); +}; +oop.inherits(Mode, TextMode); + +(function() { + + this.lineCommentStart = "#"; + + this.getNextLineIndent = function(state, line, tab) { + var indent = this.$getIndent(line); + + var tokenizedLine = this.getTokenizer().getLineTokens(line, state); + var tokens = tokenizedLine.tokens; + + if (tokens.length && tokens[tokens.length-1].type == "comment") { + return indent; + } + + if (state == "start") { + var match = line.match(/^.*[\{\(\[\:]\s*$/); + if (match) { + indent += tab; + } + } + + return indent; + }; + + var outdents = { + "pass": 1, + "return": 1, + "raise": 1, + "break": 1, + "continue": 1 + }; + + this.checkOutdent = function(state, line, input) { + if (input !== "\r\n" && input !== "\r" && input !== "\n") + return false; + + var tokens = this.getTokenizer().getLineTokens(line.trim(), state).tokens; + + if (!tokens) + return false; + do { + var last = tokens.pop(); + } while (last && (last.type == "comment" || (last.type == "text" && last.value.match(/^\s+$/)))); + + if (!last) + return false; + + return (last.type == "keyword" && outdents[last.value]); + }; + + this.autoOutdent = function(state, doc, row) { + + row += 1; + var indent = this.$getIndent(doc.getLine(row)); + var tab = doc.getTabString(); + if (indent.slice(-tab.length) == tab) + doc.remove(new Range(row, indent.length-tab.length, row, indent.length)); + }; + + this.$id = "ace/mode/python"; +}).call(Mode.prototype); + +exports.Mode = Mode; +}); + +ace.define('ace/mode/python_highlight_rules', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/mode/text_highlight_rules'], function(require, exports, module) { + + +var oop = require("../lib/oop"); +var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules; + +var PythonHighlightRules = function() { + + var keywords = ( + "and|as|assert|break|class|continue|def|del|elif|else|except|exec|" + + "finally|for|from|global|if|import|in|is|lambda|not|or|pass|print|" + + "raise|return|try|while|with|yield" + ); + + var builtinConstants = ( + "True|False|None|NotImplemented|Ellipsis|__debug__" + ); + + var builtinFunctions = ( + "abs|divmod|input|open|staticmethod|all|enumerate|int|ord|str|any|" + + "eval|isinstance|pow|sum|basestring|execfile|issubclass|print|super|" + + "binfile|iter|property|tuple|bool|filter|len|range|type|bytearray|" + + "float|list|raw_input|unichr|callable|format|locals|reduce|unicode|" + + "chr|frozenset|long|reload|vars|classmethod|getattr|map|repr|xrange|" + + "cmp|globals|max|reversed|zip|compile|hasattr|memoryview|round|" + + "__import__|complex|hash|min|set|apply|delattr|help|next|setattr|" + + "buffer|dict|hex|object|slice|coerce|dir|id|oct|sorted|intern" + ); + var keywordMapper = this.createKeywordMapper({ + "invalid.deprecated": "debugger", + "support.function": builtinFunctions, + "constant.language": builtinConstants, + "keyword": keywords + }, "identifier"); + + var strPre = "(?:r|u|ur|R|U|UR|Ur|uR)?"; + + var decimalInteger = "(?:(?:[1-9]\\d*)|(?:0))"; + var octInteger = "(?:0[oO]?[0-7]+)"; + var hexInteger = "(?:0[xX][\\dA-Fa-f]+)"; + var binInteger = "(?:0[bB][01]+)"; + var integer = "(?:" + decimalInteger + "|" + octInteger + "|" + hexInteger + "|" + binInteger + ")"; + + var exponent = "(?:[eE][+-]?\\d+)"; + var fraction = "(?:\\.\\d+)"; + var intPart = "(?:\\d+)"; + var pointFloat = "(?:(?:" + intPart + "?" + fraction + ")|(?:" + intPart + "\\.))"; + var exponentFloat = "(?:(?:" + pointFloat + "|" + intPart + ")" + exponent + ")"; + var floatNumber = "(?:" + exponentFloat + "|" + pointFloat + ")"; + + var stringEscape = "\\\\(x[0-9A-Fa-f]{2}|[0-7]{3}|[\\\\abfnrtv'\"]|U[0-9A-Fa-f]{8}|u[0-9A-Fa-f]{4})"; + + this.$rules = { + "start" : [ { + token : "comment", + regex : "#.*$" + }, { + token : "string", // multi line """ string start + regex : strPre + '"{3}', + next : "qqstring3" + }, { + token : "string", // " string + regex : strPre + '"(?=.)', + next : "qqstring" + }, { + token : "string", // multi line ''' string start + regex : strPre + "'{3}", + next : "qstring3" + }, { + token : "string", // ' string + regex : strPre + "'(?=.)", + next : "qstring" + }, { + token : "constant.numeric", // imaginary + regex : "(?:" + floatNumber + "|\\d+)[jJ]\\b" + }, { + token : "constant.numeric", // float + regex : floatNumber + }, { + token : "constant.numeric", // long integer + regex : integer + "[lL]\\b" + }, { + token : "constant.numeric", // integer + regex : integer + "\\b" + }, { + token : keywordMapper, + regex : "[a-zA-Z_$][a-zA-Z0-9_$]*\\b" + }, { + token : "keyword.operator", + regex : "\\+|\\-|\\*|\\*\\*|\\/|\\/\\/|%|<<|>>|&|\\||\\^|~|<|>|<=|=>|==|!=|<>|=" + }, { + token : "paren.lparen", + regex : "[\\[\\(\\{]" + }, { + token : "paren.rparen", + regex : "[\\]\\)\\}]" + }, { + token : "text", + regex : "\\s+" + } ], + "qqstring3" : [ { + token : "constant.language.escape", + regex : stringEscape + }, { + token : "string", // multi line """ string end + regex : '"{3}', + next : "start" + }, { + defaultToken : "string" + } ], + "qstring3" : [ { + token : "constant.language.escape", + regex : stringEscape + }, { + token : "string", // multi line ''' string end + regex : "'{3}", + next : "start" + }, { + defaultToken : "string" + } ], + "qqstring" : [{ + token : "constant.language.escape", + regex : stringEscape + }, { + token : "string", + regex : "\\\\$", + next : "qqstring" + }, { + token : "string", + regex : '"|$', + next : "start" + }, { + defaultToken: "string" + }], + "qstring" : [{ + token : "constant.language.escape", + regex : stringEscape + }, { + token : "string", + regex : "\\\\$", + next : "qstring" + }, { + token : "string", + regex : "'|$", + next : "start" + }, { + defaultToken: "string" + }] + }; +}; + +oop.inherits(PythonHighlightRules, TextHighlightRules); + +exports.PythonHighlightRules = PythonHighlightRules; +}); + +ace.define('ace/mode/folding/pythonic', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/mode/folding/fold_mode'], function(require, exports, module) { + + +var oop = require("../../lib/oop"); +var BaseFoldMode = require("./fold_mode").FoldMode; + +var FoldMode = exports.FoldMode = function(markers) { + this.foldingStartMarker = new RegExp("([\\[{])(?:\\s*)$|(" + markers + ")(?:\\s*)(?:#.*)?$"); +}; +oop.inherits(FoldMode, BaseFoldMode); + +(function() { + + this.getFoldWidgetRange = function(session, foldStyle, row) { + var line = session.getLine(row); + var match = line.match(this.foldingStartMarker); + if (match) { + if (match[1]) + return this.openingBracketBlock(session, match[1], row, match.index); + if (match[2]) + return this.indentationBlock(session, row, match.index + match[2].length); + return this.indentationBlock(session, row); + } + } + +}).call(FoldMode.prototype); + +}); diff --git a/tutor/app/assets/javascripts/editor/theme-chaos.js b/tutor/app/assets/javascripts/editor/theme-chaos.js new file mode 100644 index 00000000..a58e6a9a --- /dev/null +++ b/tutor/app/assets/javascripts/editor/theme-chaos.js @@ -0,0 +1,181 @@ +// Source: Ace editor +// Integrated by: Mimi +/* ***** BEGIN LICENSE BLOCK ***** + * Distributed under the BSD license: + * + * Copyright 2011 Irakli Gozalishvili. All rights reserved. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * ***** END LICENSE BLOCK ***** */ + +ace.define('ace/theme/chaos', ['require', 'exports', 'module' , 'ace/lib/dom'], function(require, exports, module) { + +exports.isDark = true; +exports.cssClass = "ace-chaos"; +exports.cssText = ".ace-chaos .ace_gutter {\ +background: #141414;\ +color: #595959;\ +border-right: 1px solid #282828;\ +}\ +.ace-chaos .ace_gutter-cell.ace_warning {\ +background-image: none;\ +background: #FC0;\ +border-left: none;\ +padding-left: 0;\ +color: #000;\ +}\ +.ace-chaos .ace_gutter-cell.ace_error {\ +background-position: -6px center;\ +background-image: none;\ +background: #F10;\ +border-left: none;\ +padding-left: 0;\ +color: #000;\ +}\ +.ace-chaos .ace_print-margin {\ +border-left: 1px solid #555;\ +right: 0;\ +background: #1D1D1D;\ +}\ +.ace-chaos {\ +background-color: #161616;\ +color: #E6E1DC;\ +}\ +.ace-chaos .ace_cursor {\ +border-left: 2px solid #FFFFFF;\ +}\ +.ace-chaos .ace_cursor.ace_overwrite {\ +border-left: 0px;\ +border-bottom: 1px solid #FFFFFF;\ +}\ +.ace-chaos .ace_marker-layer .ace_selection {\ +background: #494836;\ +}\ +.ace-chaos .ace_marker-layer .ace_step {\ +background: rgb(198, 219, 174);\ +}\ +.ace-chaos .ace_marker-layer .ace_bracket {\ +margin: -1px 0 0 -1px;\ +border: 1px solid #FCE94F;\ +}\ +.ace-chaos .ace_marker-layer .ace_active-line {\ +background: #333;\ +}\ +.ace-chaos .ace_gutter-active-line {\ +background-color: #222;\ +}\ +.ace-chaos .ace_invisible {\ +color: #404040;\ +}\ +.ace-chaos .ace_keyword {\ +color:#00698F;\ +}\ +.ace-chaos .ace_keyword.ace_operator {\ +color:#FF308F;\ +}\ +.ace-chaos .ace_constant {\ +color:#1EDAFB;\ +}\ +.ace-chaos .ace_constant.ace_language {\ +color:#FDC251;\ +}\ +.ace-chaos .ace_constant.ace_library {\ +color:#8DFF0A;\ +}\ +.ace-chaos .ace_constant.ace_numeric {\ +color:#58C554;\ +}\ +.ace-chaos .ace_invalid {\ +color:#FFFFFF;\ +background-color:#990000;\ +}\ +.ace-chaos .ace_invalid.ace_deprecated {\ +color:#FFFFFF;\ +background-color:#990000;\ +}\ +.ace-chaos .ace_support {\ +color: #999;\ +}\ +.ace-chaos .ace_support.ace_function {\ +color:#00AEEF;\ +}\ +.ace-chaos .ace_function {\ +color:#00AEEF;\ +}\ +.ace-chaos .ace_string {\ +color:#58C554;\ +}\ +.ace-chaos .ace_comment {\ +color:#555;\ +font-style:italic;\ +padding-bottom: 0px;\ +}\ +.ace-chaos .ace_variable {\ +color:#997744;\ +}\ +.ace-chaos .ace_meta.ace_tag {\ +color:#BE53E6;\ +}\ +.ace-chaos .ace_entity.ace_other.ace_attribute-name {\ +color:#FFFF89;\ +}\ +.ace-chaos .ace_markup.ace_underline {\ +text-decoration: underline;\ +}\ +.ace-chaos .ace_fold-widget {\ +text-align: center;\ +}\ +.ace-chaos .ace_fold-widget:hover {\ +color: #777;\ +}\ +.ace-chaos .ace_fold-widget.ace_start,\ +.ace-chaos .ace_fold-widget.ace_end,\ +.ace-chaos .ace_fold-widget.ace_closed{\ +background: none;\ +border: none;\ +box-shadow: none;\ +}\ +.ace-chaos .ace_fold-widget.ace_start:after {\ +content: '▾'\ +}\ +.ace-chaos .ace_fold-widget.ace_end:after {\ +content: '▴'\ +}\ +.ace-chaos .ace_fold-widget.ace_closed:after {\ +content: '‣'\ +}\ +.ace-chaos .ace_indent-guide {\ +border-right:1px dotted #333;\ +margin-right:-1px;\ +}\ +.ace-chaos .ace_fold { \ +background: #222; \ +border-radius: 3px; \ +color: #7AF; \ +border: none; \ +}\ +.ace-chaos .ace_fold:hover {\ +background: #CCC; \ +color: #000;\ +}\ +"; + +var dom = require("../lib/dom"); +dom.importCssString(exports.cssText, exports.cssClass); + +}); \ No newline at end of file diff --git a/tutor/app/assets/javascripts/editor/theme-chrome.js b/tutor/app/assets/javascripts/editor/theme-chrome.js new file mode 100644 index 00000000..33f83f5f --- /dev/null +++ b/tutor/app/assets/javascripts/editor/theme-chrome.js @@ -0,0 +1,160 @@ +// Source: Ace editor +// Integrated by: Mimi +/* ***** BEGIN LICENSE BLOCK ***** + * Distributed under the BSD license: + * + * Copyright (c) 2010, Ajax.org B.V. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Ajax.org B.V. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ***** END LICENSE BLOCK ***** */ + +ace.define('ace/theme/chrome', ['require', 'exports', 'module' , 'ace/lib/dom'], function(require, exports, module) { + +exports.isDark = false; +exports.cssClass = "ace-chrome"; +exports.cssText = ".ace-chrome .ace_gutter {\ +background: #ebebeb;\ +color: #333;\ +overflow : hidden;\ +}\ +.ace-chrome .ace_print-margin {\ +width: 1px;\ +background: #e8e8e8;\ +}\ +.ace-chrome {\ +background-color: #FFFFFF;\ +color: black;\ +}\ +.ace-chrome .ace_cursor {\ +color: black;\ +}\ +.ace-chrome .ace_invisible {\ +color: rgb(191, 191, 191);\ +}\ +.ace-chrome .ace_constant.ace_buildin {\ +color: rgb(88, 72, 246);\ +}\ +.ace-chrome .ace_constant.ace_language {\ +color: rgb(88, 92, 246);\ +}\ +.ace-chrome .ace_constant.ace_library {\ +color: rgb(6, 150, 14);\ +}\ +.ace-chrome .ace_invalid {\ +background-color: rgb(153, 0, 0);\ +color: white;\ +}\ +.ace-chrome .ace_fold {\ +}\ +.ace-chrome .ace_support.ace_function {\ +color: rgb(60, 76, 114);\ +}\ +.ace-chrome .ace_support.ace_constant {\ +color: rgb(6, 150, 14);\ +}\ +.ace-chrome .ace_support.ace_type,\ +.ace-chrome .ace_support.ace_class\ +.ace-chrome .ace_support.ace_other {\ +color: rgb(109, 121, 222);\ +}\ +.ace-chrome .ace_variable.ace_parameter {\ +font-style:italic;\ +color:#FD971F;\ +}\ +.ace-chrome .ace_keyword.ace_operator {\ +color: rgb(104, 118, 135);\ +}\ +.ace-chrome .ace_comment {\ +color: #236e24;\ +}\ +.ace-chrome .ace_comment.ace_doc {\ +color: #236e24;\ +}\ +.ace-chrome .ace_comment.ace_doc.ace_tag {\ +color: #236e24;\ +}\ +.ace-chrome .ace_constant.ace_numeric {\ +color: rgb(0, 0, 205);\ +}\ +.ace-chrome .ace_variable {\ +color: rgb(49, 132, 149);\ +}\ +.ace-chrome .ace_xml-pe {\ +color: rgb(104, 104, 91);\ +}\ +.ace-chrome .ace_entity.ace_name.ace_function {\ +color: #0000A2;\ +}\ +.ace-chrome .ace_heading {\ +color: rgb(12, 7, 255);\ +}\ +.ace-chrome .ace_list {\ +color:rgb(185, 6, 144);\ +}\ +.ace-chrome .ace_marker-layer .ace_selection {\ +background: rgb(181, 213, 255);\ +}\ +.ace-chrome .ace_marker-layer .ace_step {\ +background: rgb(252, 255, 0);\ +}\ +.ace-chrome .ace_marker-layer .ace_stack {\ +background: rgb(164, 229, 101);\ +}\ +.ace-chrome .ace_marker-layer .ace_bracket {\ +margin: -1px 0 0 -1px;\ +border: 1px solid rgb(192, 192, 192);\ +}\ +.ace-chrome .ace_marker-layer .ace_active-line {\ +background: rgba(0, 0, 0, 0.07);\ +}\ +.ace-chrome .ace_gutter-active-line {\ +background-color : #dcdcdc;\ +}\ +.ace-chrome .ace_marker-layer .ace_selected-word {\ +background: rgb(250, 250, 255);\ +border: 1px solid rgb(200, 200, 250);\ +}\ +.ace-chrome .ace_storage,\ +.ace-chrome .ace_keyword,\ +.ace-chrome .ace_meta.ace_tag {\ +color: rgb(147, 15, 128);\ +}\ +.ace-chrome .ace_string.ace_regex {\ +color: rgb(255, 0, 0)\ +}\ +.ace-chrome .ace_string {\ +color: #1A1AA6;\ +}\ +.ace-chrome .ace_entity.ace_other.ace_attribute-name {\ +color: #994409;\ +}\ +.ace-chrome .ace_indent-guide {\ +background: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==\") right repeat-y;\ +}\ +"; + +var dom = require("../lib/dom"); +dom.importCssString(exports.cssText, exports.cssClass); +}); diff --git a/tutor/app/assets/javascripts/editor/theme-clouds.js b/tutor/app/assets/javascripts/editor/theme-clouds.js new file mode 100644 index 00000000..9433cded --- /dev/null +++ b/tutor/app/assets/javascripts/editor/theme-clouds.js @@ -0,0 +1,128 @@ +// Source: Ace editor +// Integrated by: Mimi +/* ***** BEGIN LICENSE BLOCK ***** + * Distributed under the BSD license: + * + * Copyright (c) 2010, Ajax.org B.V. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Ajax.org B.V. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ***** END LICENSE BLOCK ***** */ + +ace.define('ace/theme/clouds', ['require', 'exports', 'module' , 'ace/lib/dom'], function(require, exports, module) { + +exports.isDark = false; +exports.cssClass = "ace-clouds"; +exports.cssText = ".ace-clouds .ace_gutter {\ +background: #ebebeb;\ +color: #333\ +}\ +.ace-clouds .ace_print-margin {\ +width: 1px;\ +background: #e8e8e8\ +}\ +.ace-clouds {\ +background-color: #FFFFFF;\ +color: #000000\ +}\ +.ace-clouds .ace_cursor {\ +color: #000000\ +}\ +.ace-clouds .ace_marker-layer .ace_selection {\ +background: #BDD5FC\ +}\ +.ace-clouds.ace_multiselect .ace_selection.ace_start {\ +box-shadow: 0 0 3px 0px #FFFFFF;\ +border-radius: 2px\ +}\ +.ace-clouds .ace_marker-layer .ace_step {\ +background: rgb(255, 255, 0)\ +}\ +.ace-clouds .ace_marker-layer .ace_bracket {\ +margin: -1px 0 0 -1px;\ +border: 1px solid #BFBFBF\ +}\ +.ace-clouds .ace_marker-layer .ace_active-line {\ +background: #FFFBD1\ +}\ +.ace-clouds .ace_gutter-active-line {\ +background-color : #dcdcdc\ +}\ +.ace-clouds .ace_marker-layer .ace_selected-word {\ +border: 1px solid #BDD5FC\ +}\ +.ace-clouds .ace_invisible {\ +color: #BFBFBF\ +}\ +.ace-clouds .ace_keyword,\ +.ace-clouds .ace_meta,\ +.ace-clouds .ace_support.ace_constant.ace_property-value {\ +color: #AF956F\ +}\ +.ace-clouds .ace_keyword.ace_operator {\ +color: #484848\ +}\ +.ace-clouds .ace_keyword.ace_other.ace_unit {\ +color: #96DC5F\ +}\ +.ace-clouds .ace_constant.ace_language {\ +color: #39946A\ +}\ +.ace-clouds .ace_constant.ace_numeric {\ +color: #46A609\ +}\ +.ace-clouds .ace_constant.ace_character.ace_entity {\ +color: #BF78CC\ +}\ +.ace-clouds .ace_invalid {\ +background-color: #FF002A\ +}\ +.ace-clouds .ace_fold {\ +background-color: #AF956F;\ +border-color: #000000\ +}\ +.ace-clouds .ace_storage,\ +.ace-clouds .ace_support.ace_class,\ +.ace-clouds .ace_support.ace_function,\ +.ace-clouds .ace_support.ace_other,\ +.ace-clouds .ace_support.ace_type {\ +color: #C52727\ +}\ +.ace-clouds .ace_string {\ +color: #5D90CD\ +}\ +.ace-clouds .ace_comment {\ +color: #BCC8BA\ +}\ +.ace-clouds .ace_entity.ace_name.ace_tag,\ +.ace-clouds .ace_entity.ace_other.ace_attribute-name {\ +color: #606060\ +}\ +.ace-clouds .ace_indent-guide {\ +background: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==\") right repeat-y\ +}"; + +var dom = require("../lib/dom"); +dom.importCssString(exports.cssText, exports.cssClass); +}); diff --git a/tutor/app/assets/javascripts/editor/theme-dreamweaver.js b/tutor/app/assets/javascripts/editor/theme-dreamweaver.js new file mode 100644 index 00000000..cdd3ae07 --- /dev/null +++ b/tutor/app/assets/javascripts/editor/theme-dreamweaver.js @@ -0,0 +1,170 @@ +// Source: Ace editor +// Integrated by: Mimi +/* ***** BEGIN LICENSE BLOCK ***** + * Distributed under the BSD license: + * + * Copyright (c) 2010, Ajax.org B.V. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Ajax.org B.V. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ***** END LICENSE BLOCK ***** */ + +ace.define('ace/theme/dreamweaver', ['require', 'exports', 'module' , 'ace/lib/dom'], function(require, exports, module) { +exports.isDark = false; +exports.cssClass = "ace-dreamweaver"; +exports.cssText = ".ace-dreamweaver .ace_gutter {\ +background: #e8e8e8;\ +color: #333;\ +}\ +.ace-dreamweaver .ace_print-margin {\ +width: 1px;\ +background: #e8e8e8;\ +}\ +.ace-dreamweaver {\ +background-color: #FFFFFF;\ +color: black;\ +}\ +.ace-dreamweaver .ace_fold {\ +background-color: #757AD8;\ +}\ +.ace-dreamweaver .ace_cursor {\ +color: black;\ +}\ +.ace-dreamweaver .ace_invisible {\ +color: rgb(191, 191, 191);\ +}\ +.ace-dreamweaver .ace_storage,\ +.ace-dreamweaver .ace_keyword {\ +color: blue;\ +}\ +.ace-dreamweaver .ace_constant.ace_buildin {\ +color: rgb(88, 72, 246);\ +}\ +.ace-dreamweaver .ace_constant.ace_language {\ +color: rgb(88, 92, 246);\ +}\ +.ace-dreamweaver .ace_constant.ace_library {\ +color: rgb(6, 150, 14);\ +}\ +.ace-dreamweaver .ace_invalid {\ +background-color: rgb(153, 0, 0);\ +color: white;\ +}\ +.ace-dreamweaver .ace_support.ace_function {\ +color: rgb(60, 76, 114);\ +}\ +.ace-dreamweaver .ace_support.ace_constant {\ +color: rgb(6, 150, 14);\ +}\ +.ace-dreamweaver .ace_support.ace_type,\ +.ace-dreamweaver .ace_support.ace_class {\ +color: #009;\ +}\ +.ace-dreamweaver .ace_support.ace_php_tag {\ +color: #f00;\ +}\ +.ace-dreamweaver .ace_keyword.ace_operator {\ +color: rgb(104, 118, 135);\ +}\ +.ace-dreamweaver .ace_string {\ +color: #00F;\ +}\ +.ace-dreamweaver .ace_comment {\ +color: rgb(76, 136, 107);\ +}\ +.ace-dreamweaver .ace_comment.ace_doc {\ +color: rgb(0, 102, 255);\ +}\ +.ace-dreamweaver .ace_comment.ace_doc.ace_tag {\ +color: rgb(128, 159, 191);\ +}\ +.ace-dreamweaver .ace_constant.ace_numeric {\ +color: rgb(0, 0, 205);\ +}\ +.ace-dreamweaver .ace_variable {\ +color: #06F\ +}\ +.ace-dreamweaver .ace_xml-pe {\ +color: rgb(104, 104, 91);\ +}\ +.ace-dreamweaver .ace_entity.ace_name.ace_function {\ +color: #00F;\ +}\ +.ace-dreamweaver .ace_heading {\ +color: rgb(12, 7, 255);\ +}\ +.ace-dreamweaver .ace_list {\ +color:rgb(185, 6, 144);\ +}\ +.ace-dreamweaver .ace_marker-layer .ace_selection {\ +background: rgb(181, 213, 255);\ +}\ +.ace-dreamweaver .ace_marker-layer .ace_step {\ +background: rgb(252, 255, 0);\ +}\ +.ace-dreamweaver .ace_marker-layer .ace_stack {\ +background: rgb(164, 229, 101);\ +}\ +.ace-dreamweaver .ace_marker-layer .ace_bracket {\ +margin: -1px 0 0 -1px;\ +border: 1px solid rgb(192, 192, 192);\ +}\ +.ace-dreamweaver .ace_marker-layer .ace_active-line {\ +background: rgba(0, 0, 0, 0.07);\ +}\ +.ace-dreamweaver .ace_marker-layer .ace_selected-word {\ +background: rgb(250, 250, 255);\ +border: 1px solid rgb(200, 200, 250);\ +}\ +.ace-dreamweaver .ace_meta.ace_tag {\ +color:#009;\ +}\ +.ace-dreamweaver .ace_meta.ace_tag.ace_anchor {\ +color:#060;\ +}\ +.ace-dreamweaver .ace_meta.ace_tag.ace_form {\ +color:#F90;\ +}\ +.ace-dreamweaver .ace_meta.ace_tag.ace_image {\ +color:#909;\ +}\ +.ace-dreamweaver .ace_meta.ace_tag.ace_script {\ +color:#900;\ +}\ +.ace-dreamweaver .ace_meta.ace_tag.ace_style {\ +color:#909;\ +}\ +.ace-dreamweaver .ace_meta.ace_tag.ace_table {\ +color:#099;\ +}\ +.ace-dreamweaver .ace_string.ace_regex {\ +color: rgb(255, 0, 0)\ +}\ +.ace-dreamweaver .ace_indent-guide {\ +background: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==\") right repeat-y;\ +}"; + +var dom = require("../lib/dom"); +dom.importCssString(exports.cssText, exports.cssClass); +}); diff --git a/tutor/app/assets/javascripts/editor/theme-eclipse.js b/tutor/app/assets/javascripts/editor/theme-eclipse.js new file mode 100644 index 00000000..c9bbdd0a --- /dev/null +++ b/tutor/app/assets/javascripts/editor/theme-eclipse.js @@ -0,0 +1,127 @@ +// Source: Ace editor +// Integrated by: Mimi +/* ***** BEGIN LICENSE BLOCK ***** + * Distributed under the BSD license: + * + * Copyright (c) 2010, Ajax.org B.V. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Ajax.org B.V. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ***** END LICENSE BLOCK ***** */ + +ace.define('ace/theme/eclipse', ['require', 'exports', 'module' , 'ace/lib/dom'], function(require, exports, module) { + + +exports.isDark = false; +exports.cssText = ".ace-eclipse .ace_gutter {\ +background: #ebebeb;\ +border-right: 1px solid rgb(159, 159, 159);\ +color: rgb(136, 136, 136);\ +}\ +.ace-eclipse .ace_print-margin {\ +width: 1px;\ +background: #ebebeb;\ +}\ +.ace-eclipse {\ +background-color: #FFFFFF;\ +color: black;\ +}\ +.ace-eclipse .ace_fold {\ +background-color: rgb(60, 76, 114);\ +}\ +.ace-eclipse .ace_cursor {\ +color: black;\ +}\ +.ace-eclipse .ace_storage,\ +.ace-eclipse .ace_keyword,\ +.ace-eclipse .ace_variable {\ +color: rgb(127, 0, 85);\ +}\ +.ace-eclipse .ace_constant.ace_buildin {\ +color: rgb(88, 72, 246);\ +}\ +.ace-eclipse .ace_constant.ace_library {\ +color: rgb(6, 150, 14);\ +}\ +.ace-eclipse .ace_function {\ +color: rgb(60, 76, 114);\ +}\ +.ace-eclipse .ace_string {\ +color: rgb(42, 0, 255);\ +}\ +.ace-eclipse .ace_comment {\ +color: rgb(113, 150, 130);\ +}\ +.ace-eclipse .ace_comment.ace_doc {\ +color: rgb(63, 95, 191);\ +}\ +.ace-eclipse .ace_comment.ace_doc.ace_tag {\ +color: rgb(127, 159, 191);\ +}\ +.ace-eclipse .ace_constant.ace_numeric {\ +color: darkblue;\ +}\ +.ace-eclipse .ace_tag {\ +color: rgb(25, 118, 116);\ +}\ +.ace-eclipse .ace_type {\ +color: rgb(127, 0, 127);\ +}\ +.ace-eclipse .ace_xml-pe {\ +color: rgb(104, 104, 91);\ +}\ +.ace-eclipse .ace_marker-layer .ace_selection {\ +background: rgb(181, 213, 255);\ +}\ +.ace-eclipse .ace_marker-layer .ace_bracket {\ +margin: -1px 0 0 -1px;\ +border: 1px solid rgb(192, 192, 192);\ +}\ +.ace-eclipse .ace_meta.ace_tag {\ +color:rgb(25, 118, 116);\ +}\ +.ace-eclipse .ace_invisible {\ +color: #ddd;\ +}\ +.ace-eclipse .ace_entity.ace_other.ace_attribute-name {\ +color:rgb(127, 0, 127);\ +}\ +.ace-eclipse .ace_marker-layer .ace_step {\ +background: rgb(255, 255, 0);\ +}\ +.ace-eclipse .ace_marker-layer .ace_active-line {\ +background: rgb(232, 242, 254);\ +}\ +.ace-eclipse .ace_marker-layer .ace_selected-word {\ +border: 1px solid rgb(181, 213, 255);\ +}\ +.ace-eclipse .ace_indent-guide {\ +background: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==\") right repeat-y;\ +}"; + +exports.cssClass = "ace-eclipse"; + +var dom = require("../lib/dom"); +dom.importCssString(exports.cssText, exports.cssClass); +}); diff --git a/tutor/app/assets/javascripts/editor/theme-github.js b/tutor/app/assets/javascripts/editor/theme-github.js new file mode 100644 index 00000000..8f344b54 --- /dev/null +++ b/tutor/app/assets/javascripts/editor/theme-github.js @@ -0,0 +1,133 @@ +// Source: Ace editor +// Integrated by: Mimi +/* ***** BEGIN LICENSE BLOCK ***** + * Distributed under the BSD license: + * + * Copyright (c) 2010, Ajax.org B.V. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Ajax.org B.V. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ***** END LICENSE BLOCK ***** */ + +ace.define('ace/theme/github', ['require', 'exports', 'module' , 'ace/lib/dom'], function(require, exports, module) { + +exports.isDark = false; +exports.cssClass = "ace-github"; +exports.cssText = "/* CSS style content from github's default pygments highlighter template.\ +Cursor and selection styles from textmate.css. */\ +.ace-github .ace_gutter {\ +background: #e8e8e8;\ +color: #AAA;\ +}\ +.ace-github {\ +background: #fff;\ +color: #000;\ +}\ +.ace-github .ace_keyword {\ +font-weight: bold;\ +}\ +.ace-github .ace_string {\ +color: #D14;\ +}\ +.ace-github .ace_variable.ace_class {\ +color: teal;\ +}\ +.ace-github .ace_constant.ace_numeric {\ +color: #099;\ +}\ +.ace-github .ace_constant.ace_buildin {\ +color: #0086B3;\ +}\ +.ace-github .ace_support.ace_function {\ +color: #0086B3;\ +}\ +.ace-github .ace_comment {\ +color: #998;\ +font-style: italic;\ +}\ +.ace-github .ace_variable.ace_language {\ +color: #0086B3;\ +}\ +.ace-github .ace_paren {\ +font-weight: bold;\ +}\ +.ace-github .ace_boolean {\ +font-weight: bold;\ +}\ +.ace-github .ace_string.ace_regexp {\ +color: #009926;\ +font-weight: normal;\ +}\ +.ace-github .ace_variable.ace_instance {\ +color: teal;\ +}\ +.ace-github .ace_constant.ace_language {\ +font-weight: bold;\ +}\ +.ace-github .ace_cursor {\ +color: black;\ +}\ +.ace-github .ace_marker-layer .ace_active-line {\ +background: rgb(255, 255, 204);\ +}\ +.ace-github .ace_marker-layer .ace_selection {\ +background: rgb(181, 213, 255);\ +}\ +.ace-github.ace_multiselect .ace_selection.ace_start {\ +box-shadow: 0 0 3px 0px white;\ +border-radius: 2px;\ +}\ +/* bold keywords cause cursor issues for some fonts */\ +/* this disables bold style for editor and keeps for static highlighter */\ +.ace-github.ace_nobold .ace_line > span {\ +font-weight: normal !important;\ +}\ +.ace-github .ace_marker-layer .ace_step {\ +background: rgb(252, 255, 0);\ +}\ +.ace-github .ace_marker-layer .ace_stack {\ +background: rgb(164, 229, 101);\ +}\ +.ace-github .ace_marker-layer .ace_bracket {\ +margin: -1px 0 0 -1px;\ +border: 1px solid rgb(192, 192, 192);\ +}\ +.ace-github .ace_gutter-active-line {\ +background-color : rgba(0, 0, 0, 0.07);\ +}\ +.ace-github .ace_marker-layer .ace_selected-word {\ +background: rgb(250, 250, 255);\ +border: 1px solid rgb(200, 200, 250);\ +}\ +.ace-github .ace_print-margin {\ +width: 1px;\ +background: #e8e8e8;\ +}\ +.ace-github .ace_indent-guide {\ +background: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==\") right repeat-y;\ +}"; + + var dom = require("../lib/dom"); + dom.importCssString(exports.cssText, exports.cssClass); +}); diff --git a/tutor/app/assets/javascripts/editor/theme-tomorrow_night_blue.js b/tutor/app/assets/javascripts/editor/theme-tomorrow_night_blue.js new file mode 100644 index 00000000..3782918c --- /dev/null +++ b/tutor/app/assets/javascripts/editor/theme-tomorrow_night_blue.js @@ -0,0 +1,139 @@ +// Source: Ace editor +// Integrated by: Mimi +/* ***** BEGIN LICENSE BLOCK ***** + * Distributed under the BSD license: + * + * Copyright (c) 2010, Ajax.org B.V. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Ajax.org B.V. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ***** END LICENSE BLOCK ***** */ + +ace.define('ace/theme/tomorrow_night_blue', ['require', 'exports', 'module' , 'ace/lib/dom'], function(require, exports, module) { + +exports.isDark = true; +exports.cssClass = "ace-tomorrow-night-blue"; +exports.cssText = ".ace-tomorrow-night-blue .ace_gutter {\ +background: #00204b;\ +color: #7388b5\ +}\ +.ace-tomorrow-night-blue .ace_print-margin {\ +width: 1px;\ +background: #00204b\ +}\ +.ace-tomorrow-night-blue {\ +background-color: #002451;\ +color: #FFFFFF\ +}\ +.ace-tomorrow-night-blue .ace_constant.ace_other,\ +.ace-tomorrow-night-blue .ace_cursor {\ +color: #FFFFFF\ +}\ +.ace-tomorrow-night-blue .ace_marker-layer .ace_selection {\ +background: #003F8E\ +}\ +.ace-tomorrow-night-blue.ace_multiselect .ace_selection.ace_start {\ +box-shadow: 0 0 3px 0px #002451;\ +border-radius: 2px\ +}\ +.ace-tomorrow-night-blue .ace_marker-layer .ace_step {\ +background: rgb(127, 111, 19)\ +}\ +.ace-tomorrow-night-blue .ace_marker-layer .ace_bracket {\ +margin: -1px 0 0 -1px;\ +border: 1px solid #404F7D\ +}\ +.ace-tomorrow-night-blue .ace_marker-layer .ace_active-line {\ +background: #00346E\ +}\ +.ace-tomorrow-night-blue .ace_gutter-active-line {\ +background-color: #022040\ +}\ +.ace-tomorrow-night-blue .ace_marker-layer .ace_selected-word {\ +border: 1px solid #003F8E\ +}\ +.ace-tomorrow-night-blue .ace_invisible {\ +color: #404F7D\ +}\ +.ace-tomorrow-night-blue .ace_keyword,\ +.ace-tomorrow-night-blue .ace_meta,\ +.ace-tomorrow-night-blue .ace_storage,\ +.ace-tomorrow-night-blue .ace_storage.ace_type,\ +.ace-tomorrow-night-blue .ace_support.ace_type {\ +color: #EBBBFF\ +}\ +.ace-tomorrow-night-blue .ace_keyword.ace_operator {\ +color: #99FFFF\ +}\ +.ace-tomorrow-night-blue .ace_constant.ace_character,\ +.ace-tomorrow-night-blue .ace_constant.ace_language,\ +.ace-tomorrow-night-blue .ace_constant.ace_numeric,\ +.ace-tomorrow-night-blue .ace_keyword.ace_other.ace_unit,\ +.ace-tomorrow-night-blue .ace_support.ace_constant,\ +.ace-tomorrow-night-blue .ace_variable.ace_parameter {\ +color: #FFC58F\ +}\ +.ace-tomorrow-night-blue .ace_invalid {\ +color: #FFFFFF;\ +background-color: #F99DA5\ +}\ +.ace-tomorrow-night-blue .ace_invalid.ace_deprecated {\ +color: #FFFFFF;\ +background-color: #EBBBFF\ +}\ +.ace-tomorrow-night-blue .ace_fold {\ +background-color: #BBDAFF;\ +border-color: #FFFFFF\ +}\ +.ace-tomorrow-night-blue .ace_entity.ace_name.ace_function,\ +.ace-tomorrow-night-blue .ace_support.ace_function,\ +.ace-tomorrow-night-blue .ace_variable {\ +color: #BBDAFF\ +}\ +.ace-tomorrow-night-blue .ace_support.ace_class,\ +.ace-tomorrow-night-blue .ace_support.ace_type {\ +color: #FFEEAD\ +}\ +.ace-tomorrow-night-blue .ace_heading,\ +.ace-tomorrow-night-blue .ace_markup.ace_heading,\ +.ace-tomorrow-night-blue .ace_string {\ +color: #D1F1A9\ +}\ +.ace-tomorrow-night-blue .ace_entity.ace_name.ace_tag,\ +.ace-tomorrow-night-blue .ace_entity.ace_other.ace_attribute-name,\ +.ace-tomorrow-night-blue .ace_meta.ace_tag,\ +.ace-tomorrow-night-blue .ace_string.ace_regexp,\ +.ace-tomorrow-night-blue .ace_variable {\ +color: #FF9DA4\ +}\ +.ace-tomorrow-night-blue .ace_comment {\ +color: #7285B7\ +}\ +.ace-tomorrow-night-blue .ace_indent-guide {\ +background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWNgYGBgYJDzqfwPAANXAeNsiA+ZAAAAAElFTkSuQmCC) right repeat-y\ +}"; + +var dom = require("../lib/dom"); +dom.importCssString(exports.cssText, exports.cssClass); +}); diff --git a/tutor/app/assets/javascripts/editor/theme-tomorrow_night_eighties.js b/tutor/app/assets/javascripts/editor/theme-tomorrow_night_eighties.js new file mode 100644 index 00000000..ef7360f7 --- /dev/null +++ b/tutor/app/assets/javascripts/editor/theme-tomorrow_night_eighties.js @@ -0,0 +1,141 @@ +// Source: Ace editor +// Integrated by: Mimi +/* ***** BEGIN LICENSE BLOCK ***** + * Distributed under the BSD license: + * + * Copyright (c) 2010, Ajax.org B.V. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Ajax.org B.V. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ***** END LICENSE BLOCK ***** */ + +ace.define('ace/theme/tomorrow_night_eighties', ['require', 'exports', 'module' , 'ace/lib/dom'], function(require, exports, module) { + +exports.isDark = true; +exports.cssClass = "ace-tomorrow-night-eighties"; +exports.cssText = ".ace-tomorrow-night-eighties .ace_gutter {\ +background: #272727;\ +color: #CCC\ +}\ +.ace-tomorrow-night-eighties .ace_print-margin {\ +width: 1px;\ +background: #272727\ +}\ +.ace-tomorrow-night-eighties {\ +background-color: #2D2D2D;\ +color: #CCCCCC\ +}\ +.ace-tomorrow-night-eighties .ace_constant.ace_other,\ +.ace-tomorrow-night-eighties .ace_cursor {\ +color: #CCCCCC\ +}\ +.ace-tomorrow-night-eighties .ace_marker-layer .ace_selection {\ +background: #515151\ +}\ +.ace-tomorrow-night-eighties.ace_multiselect .ace_selection.ace_start {\ +box-shadow: 0 0 3px 0px #2D2D2D;\ +border-radius: 2px\ +}\ +.ace-tomorrow-night-eighties .ace_marker-layer .ace_step {\ +background: rgb(102, 82, 0)\ +}\ +.ace-tomorrow-night-eighties .ace_marker-layer .ace_bracket {\ +margin: -1px 0 0 -1px;\ +border: 1px solid #6A6A6A\ +}\ +.ace-tomorrow-night-bright .ace_stack {\ +background: rgb(66, 90, 44)\ +}\ +.ace-tomorrow-night-eighties .ace_marker-layer .ace_active-line {\ +background: #393939\ +}\ +.ace-tomorrow-night-eighties .ace_gutter-active-line {\ +background-color: #393939\ +}\ +.ace-tomorrow-night-eighties .ace_marker-layer .ace_selected-word {\ +border: 1px solid #515151\ +}\ +.ace-tomorrow-night-eighties .ace_invisible {\ +color: #6A6A6A\ +}\ +.ace-tomorrow-night-eighties .ace_keyword,\ +.ace-tomorrow-night-eighties .ace_meta,\ +.ace-tomorrow-night-eighties .ace_storage,\ +.ace-tomorrow-night-eighties .ace_storage.ace_type,\ +.ace-tomorrow-night-eighties .ace_support.ace_type {\ +color: #CC99CC\ +}\ +.ace-tomorrow-night-eighties .ace_keyword.ace_operator {\ +color: #66CCCC\ +}\ +.ace-tomorrow-night-eighties .ace_constant.ace_character,\ +.ace-tomorrow-night-eighties .ace_constant.ace_language,\ +.ace-tomorrow-night-eighties .ace_constant.ace_numeric,\ +.ace-tomorrow-night-eighties .ace_keyword.ace_other.ace_unit,\ +.ace-tomorrow-night-eighties .ace_support.ace_constant,\ +.ace-tomorrow-night-eighties .ace_variable.ace_parameter {\ +color: #F99157\ +}\ +.ace-tomorrow-night-eighties .ace_invalid {\ +color: #CDCDCD;\ +background-color: #F2777A\ +}\ +.ace-tomorrow-night-eighties .ace_invalid.ace_deprecated {\ +color: #CDCDCD;\ +background-color: #CC99CC\ +}\ +.ace-tomorrow-night-eighties .ace_fold {\ +background-color: #6699CC;\ +border-color: #CCCCCC\ +}\ +.ace-tomorrow-night-eighties .ace_entity.ace_name.ace_function,\ +.ace-tomorrow-night-eighties .ace_support.ace_function,\ +.ace-tomorrow-night-eighties .ace_variable {\ +color: #6699CC\ +}\ +.ace-tomorrow-night-eighties .ace_support.ace_class,\ +.ace-tomorrow-night-eighties .ace_support.ace_type {\ +color: #FFCC66\ +}\ +.ace-tomorrow-night-eighties .ace_heading,\ +.ace-tomorrow-night-eighties .ace_markup.ace_heading,\ +.ace-tomorrow-night-eighties .ace_string {\ +color: #99CC99\ +}\ +.ace-tomorrow-night-eighties .ace_comment {\ +color: #999999\ +}\ +.ace-tomorrow-night-eighties .ace_entity.ace_name.ace_tag,\ +.ace-tomorrow-night-eighties .ace_entity.ace_other.ace_attribute-name,\ +.ace-tomorrow-night-eighties .ace_meta.ace_tag,\ +.ace-tomorrow-night-eighties .ace_variable {\ +color: #F2777A\ +}\ +.ace-tomorrow-night-eighties .ace_indent-guide {\ +background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWPQ09NrYAgMjP4PAAtGAwchHMyAAAAAAElFTkSuQmCC) right repeat-y\ +}"; + +var dom = require("../lib/dom"); +dom.importCssString(exports.cssText, exports.cssClass); +}); diff --git a/tutor/app/assets/javascripts/theme-twilight.js b/tutor/app/assets/javascripts/editor/theme-twilight.js similarity index 99% rename from tutor/app/assets/javascripts/theme-twilight.js rename to tutor/app/assets/javascripts/editor/theme-twilight.js index 5376cecb..f02d3aa1 100644 --- a/tutor/app/assets/javascripts/theme-twilight.js +++ b/tutor/app/assets/javascripts/editor/theme-twilight.js @@ -1,5 +1,7 @@ // Source: Ace editor // Integrated by: Mimi +// Source: Ace editor +// Integrated by: Mimi /* ***** BEGIN LICENSE BLOCK ***** * Distributed under the BSD license: * diff --git a/tutor/app/assets/javascripts/editor/theme-xcode.js b/tutor/app/assets/javascripts/editor/theme-xcode.js new file mode 100644 index 00000000..1abacd93 --- /dev/null +++ b/tutor/app/assets/javascripts/editor/theme-xcode.js @@ -0,0 +1,121 @@ +// Source: Ace editor +// Integrated by: Mimi +/* ***** BEGIN LICENSE BLOCK ***** + * Distributed under the BSD license: + * + * Copyright (c) 2010, Ajax.org B.V. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Ajax.org B.V. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ***** END LICENSE BLOCK ***** */ + +ace.define('ace/theme/xcode', ['require', 'exports', 'module' , 'ace/lib/dom'], function(require, exports, module) { + +exports.isDark = false; +exports.cssClass = "ace-xcode"; +exports.cssText = "/* THIS THEME WAS AUTOGENERATED BY Theme.tmpl.css (UUID: EE3AD170-2B7F-4DE1-B724-C75F13FE0085) */\ +.ace-xcode .ace_gutter {\ +background: #e8e8e8;\ +color: #333\ +}\ +.ace-xcode .ace_print-margin {\ +width: 1px;\ +background: #e8e8e8\ +}\ +.ace-xcode {\ +background-color: #FFFFFF;\ +color: #000000\ +}\ +.ace-xcode .ace_cursor {\ +color: #000000\ +}\ +.ace-xcode .ace_marker-layer .ace_selection {\ +background: #B5D5FF\ +}\ +.ace-xcode.ace_multiselect .ace_selection.ace_start {\ +box-shadow: 0 0 3px 0px #FFFFFF;\ +border-radius: 2px\ +}\ +.ace-xcode .ace_marker-layer .ace_step {\ +background: rgb(198, 219, 174)\ +}\ +.ace-xcode .ace_marker-layer .ace_bracket {\ +margin: -1px 0 0 -1px;\ +border: 1px solid #BFBFBF\ +}\ +.ace-xcode .ace_marker-layer .ace_active-line {\ +background: rgba(0, 0, 0, 0.071)\ +}\ +.ace-xcode .ace_gutter-active-line {\ +background-color: rgba(0, 0, 0, 0.071)\ +}\ +.ace-xcode .ace_marker-layer .ace_selected-word {\ +border: 1px solid #B5D5FF\ +}\ +.ace-xcode .ace_constant.ace_language,\ +.ace-xcode .ace_keyword,\ +.ace-xcode .ace_meta,\ +.ace-xcode .ace_variable.ace_language {\ +color: #C800A4\ +}\ +.ace-xcode .ace_invisible {\ +color: #BFBFBF\ +}\ +.ace-xcode .ace_constant.ace_character,\ +.ace-xcode .ace_constant.ace_other {\ +color: #275A5E\ +}\ +.ace-xcode .ace_constant.ace_numeric {\ +color: #3A00DC\ +}\ +.ace-xcode .ace_entity.ace_other.ace_attribute-name,\ +.ace-xcode .ace_support.ace_constant,\ +.ace-xcode .ace_support.ace_function {\ +color: #450084\ +}\ +.ace-xcode .ace_fold {\ +background-color: #C800A4;\ +border-color: #000000\ +}\ +.ace-xcode .ace_entity.ace_name.ace_tag,\ +.ace-xcode .ace_support.ace_class,\ +.ace-xcode .ace_support.ace_type {\ +color: #790EAD\ +}\ +.ace-xcode .ace_storage {\ +color: #C900A4\ +}\ +.ace-xcode .ace_string {\ +color: #DF0002\ +}\ +.ace-xcode .ace_comment {\ +color: #008E00\ +}\ +.ace-xcode .ace_indent-guide {\ +background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==) right repeat-y\ +}"; + +var dom = require("../lib/dom"); +dom.importCssString(exports.cssText, exports.cssClass); +}); diff --git a/tutor/app/assets/javascripts/notes.js.coffee b/tutor/app/assets/javascripts/notes.js.coffee new file mode 100644 index 00000000..24f83d18 --- /dev/null +++ b/tutor/app/assets/javascripts/notes.js.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/tutor/app/assets/javascripts/problems.js.coffee b/tutor/app/assets/javascripts/problems.js.coffee index d27fb224..a537d1c2 100644 --- a/tutor/app/assets/javascripts/problems.js.coffee +++ b/tutor/app/assets/javascripts/problems.js.coffee @@ -24,12 +24,12 @@ timer = -> document.getElementById("secs").innerHTML = digit i = 0 while true - hint = $('#hint' + i) - if typeof(hint.attr 'time') == 'undefined' + tip = $('#tip' + i) + if typeof(tip.attr 'time') == 'undefined' break - time = hint.attr 'time' + time = tip.attr 'time' if time <= min*60 + parseInt(digit, 10) - hint.attr 'class', '' + tip.attr 'class', '' i++ activate = -> diff --git a/tutor/app/assets/javascripts/solutions.js.coffee b/tutor/app/assets/javascripts/solutions.js.coffee index 910eeb33..ae0df5de 100644 --- a/tutor/app/assets/javascripts/solutions.js.coffee +++ b/tutor/app/assets/javascripts/solutions.js.coffee @@ -2,34 +2,39 @@ variables = null # The number of the current index of the line to be executed index_number = 0 +# The status of the debugging session +status = null # [Debugger: Debug - Story 3.6] # Sends the code to the server and changes the Variables to the data recieved # Parameters: # problem_id: The id of the problem being solved +# problem_type: The type of the problem to be submitted # Returns: none # Author: Mussab ElDash -@start_debug = (problem_id) -> - input = $('#solution_code').val() +@start_debug = (problem_id, problem_type) -> + input = get_editor_session().getValue() if input.length is 0 alert "You didn't write any code" return test = $('#solution_input').val() + class_name = $("#class_name").val() start_spin() toggle_code_area() $.ajax type: "POST" url: '/debuggers/' + problem_id - data: {code : input , case : test} + data: {code: input, case: test, class_name: class_name,\ + problem_type: problem_type} datatype: 'json' success: (data) -> clear_console() - variables = data stop_spin() if !data["success"] compilation_error data["data"]["errors"] return variables = data["data"] + status = data["status"] toggleDebug(1) debug_console() jump_state 0 @@ -44,11 +49,13 @@ index_number = 0 # Sends the code to the server to be compiled # Parameters: # problem_id: The id of the problem being solved +# problem_type: The type of the problem to be submitted # Returns: # none # Author: Ahmed Akram -@compile = (problem_id) -> - input = $('#solution_code').val() +@compile = (problem_id, problem_type) -> + input = get_editor_session().getValue() + class_name = $("#class_name").val() if input.length is 0 alert "You didn't write any code" return @@ -57,7 +64,8 @@ index_number = 0 $.ajax type: "POST" url: '/solutions/compile_solution' - data: {code : input, problem_id : problem_id} + data: {code: input, problem_id: problem_id,\ + class_name: class_name, problem_type: problem_type} datatype: 'json' success: (unique) -> clear_console() @@ -78,21 +86,24 @@ index_number = 0 # Sends the code and the test case to the server to be tested # Parameters: # problem_id: The id of the problem being solved +# problem_type: The type of the problem to be submitted # Returns: # none # Author: Ahmed Akram -@run_input = (problem_id) -> - code = $('#solution_code').val() +@run_input = (problem_id, problem_type) -> + code = get_editor_session().getValue() if code.length is 0 alert "You didn't write any code" return test = $('#solution_input').val() + class_name = $("#class_name").val() start_spin() toggle_code_area() $.ajax type: "POST" url: '/solutions/execute' - data: {code : code, problem_id : problem_id, input : test} + data: {code: code, problem_id: problem_id, input: test,\ + class_name: class_name, problem_type: problem_type} datatype: 'json' success: (data) -> clear_console() @@ -222,7 +233,7 @@ debug_console = -> $('#nextButton').toggle(state) $('#previousButton').toggle(state) $('#stopButton').toggle(state) - editor = ace.edit("editor") + editor = get_editor() editor.setReadOnly(state) normal_theme = "ace/theme/twilight" debug_theme = normal_theme + "-debug" @@ -244,7 +255,7 @@ debug_console = -> alert "The online debugger can only step forward 100 times." jump_state index_number else - alert "The program has terminated." + alert status jump_state index_number # [Execute Line By Line - Story 3.8] @@ -266,7 +277,7 @@ debug_console = -> # Returns: none # Author: Rami Khalil @highlight_line = (line) -> - editor = ace.edit("editor") + editor = get_editor() editor.gotoLine line, 0, true # [Execute Line By Line - Story 3.8] @@ -322,24 +333,27 @@ debug_console = -> # using ajax showing an alert box for success and failure scenarios # Parameters: # problem_id: the id of the problem being solved +# problem_type: The type of the problem to be submitted # Returns: a json object containing two arrays one for the errors # of the current code and the other containing success messages # and the success and failure messages are displayed in a table # Author: MOHAMEDSAEED -@validate_code = (problem_id) -> - code = $('#solution_code').val() +@validate_code = (problem_id, problem_type) -> + code = get_editor_session().getValue() mins = parseInt($('#mins').text()) secs = parseInt($('#secs').text()) time = (mins * 60) + secs if code.length is 0 alert 'Blank submissions are not allowed' return + class_name = $("#class_name").val() toggle_code_area() start_spin() $.ajax type: "POST" url: '/solutions' - data: {problem_id: problem_id, code: code, time: time} + data: {problem_id: problem_id, code: code, time: time,\ + class_name: class_name, problem_type: problem_type} datatype: 'json' success: (data) -> clear_console() @@ -379,10 +393,17 @@ debug_console = -> # Parameters: none # Returns: none # Author: MOHAMEDSAEED -@reload_template = () -> - editor = ace.edit("editor"); - edit_session = editor.getSession(); - template = "public class CoolSoft {\n" - template += "\tpublic static void main(String [] args) {\n\t\t\n\t}\n}" - edit_session.setValue(template); - return \ No newline at end of file +@reload_template = () -> + disabled = $('#solution_code').prop 'disabled' + unless disabled + template = "public class CoolSoft {\n" + template += "\tpublic static void main(String [] args) {\n\t\t\n\t}\n}" + get_editor_session().setValue(template); + $('#class_name').val("CoolSoft"); + return + +get_editor = -> + ace.edit("editor") + +get_editor_session = -> + get_editor().getSession() diff --git a/tutor/app/assets/javascripts/topics.js.coffee b/tutor/app/assets/javascripts/topics.js.coffee index 6891ae5b..30016472 100644 --- a/tutor/app/assets/javascripts/topics.js.coffee +++ b/tutor/app/assets/javascripts/topics.js.coffee @@ -3,7 +3,12 @@ # Author: Mussab ElDash @newTrack = -> create = document.getElementById("create_track") + new_track = document.getElementById("new_track") create.hidden = !create.hidden + if create.hidden + new_track.innerText = "New" + else + new_track.innerText = "Hide" return # [Edit Track Rating - Story 4.13] diff --git a/tutor/app/assets/stylesheets/application.css b/tutor/app/assets/stylesheets/application.css index d13f78b7..fd4dc0eb 100644 --- a/tutor/app/assets/stylesheets/application.css +++ b/tutor/app/assets/stylesheets/application.css @@ -10,6 +10,7 @@ * *= require_self *= require_tree . + *= require bootstrap */ = require posts @@ -37,6 +38,7 @@ .footer { width:100%; border-radius: 2px; + margin-top: 1.65%; } .auto-height { @@ -54,4 +56,39 @@ .home { overflow:auto; +} + +.zooming { + -ms-transform: scale(0.8); /* IE 9 */ + -ms-transform-origin: left top; + -webkit-transform: scale(0.8); /* Chrome, Safari, Opera */ + -webkit-transform-origin: left top; + transform: scale(0.8); + transform-origin: left top; + -moz-transform: scale(0.8); + -moz-transform-origin: left top; +} + +.unzooming, #editor{ + -ms-transform: scale(1.25); /* IE 9 */ + -ms-transform-origin: left top; + -webkit-transform: scale(1.25); /* Chrome, Safari, Opera */ + -webkit-transform-origin: left top; + transform: scale(1.25); + transform-origin: left top; + -moz-transform: scale(1.25); + -moz-transform-origin: left top; +} + +.zoom125{ + height: 125%; + width: 125%; +} + +#editor{ + width: 80%; +} + +.no-scroll-x{ + overflow-x: hidden; } \ No newline at end of file diff --git a/tutor/app/assets/stylesheets/assignment_problems.css.scss b/tutor/app/assets/stylesheets/assignment_problems.css.scss new file mode 100644 index 00000000..6c584478 --- /dev/null +++ b/tutor/app/assets/stylesheets/assignment_problems.css.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the assignment_problems controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/tutor/app/assets/stylesheets/assignments.css.scss b/tutor/app/assets/stylesheets/assignments.css.scss new file mode 100644 index 00000000..193577b0 --- /dev/null +++ b/tutor/app/assets/stylesheets/assignments.css.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the assignments controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/tutor/app/assets/stylesheets/notes.css.scss b/tutor/app/assets/stylesheets/notes.css.scss new file mode 100644 index 00000000..56d31f58 --- /dev/null +++ b/tutor/app/assets/stylesheets/notes.css.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the notes controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/tutor/app/assets/stylesheets/solutions.css b/tutor/app/assets/stylesheets/solutions.css index ab2e5dc5..55fd94b9 100644 --- a/tutor/app/assets/stylesheets/solutions.css +++ b/tutor/app/assets/stylesheets/solutions.css @@ -42,14 +42,12 @@ textarea { .fixed-content { max-height: 200px; - overflow-y:scroll; - overflow-x:hidden; + overflow: auto; } .fixed-horizontal { max-width: 175px; - overflow-x:scroll; - overflow-y:hidden; + overflow: auto; } .editor { @@ -74,9 +72,7 @@ textarea { max-height: 150px; min-height: 150px; background: white; - overflow: scroll; - overflow-y: scroll; - overflow-x: scroll; + overflow: auto; max-width: 750px; min-width: 750px; } @@ -118,4 +114,38 @@ textarea { .hidden { visibility: false; +} + +.edit_button { + width: 20%; + height: 1%; + margin_top: 10px; + cursor: pointer; +} + +.add_note_button { + opacity: 0; + width: 25%; + height: 15%; + margin_top: 10px; + cursor: pointer; +} + +.form_div { + display: none; + width: 60%; +} + +.x_button { + width: 18%; + height: 15%; + margin_top: 10px; + cursor: default; +} + +.tick_button { + width: 20%; + height: 20%; + margin_top: 10px; + cursor: default; } \ No newline at end of file diff --git a/tutor/app/controllers/assignment_problems_controller.rb b/tutor/app/controllers/assignment_problems_controller.rb new file mode 100644 index 00000000..96e716b7 --- /dev/null +++ b/tutor/app/controllers/assignment_problems_controller.rb @@ -0,0 +1,39 @@ +class AssignmentProblemsController < ApplicationController + # [View List Of Assignments - Story 4.24] + # Lists the list of problems in a + # particular assignment + # Parameters: + # id: Id of the problem. + # Returns: none + # Author: Lin Kassem + def show + @problem = AssignmentProblem.find_by_id(params[:id]) + if @problem.nil? + render "problem_not_found" + else + @assignment = @problem.assignment + @course = @assignment.course + @can_edit = @course.can_edit(current_lecturer) + @can_edit||= @course.can_edit(current_teaching_assistant) + end + end + + # [View List Of Assignments - Story 4.24] + # Allows the editing of a particular problem + # in a particular assignment + # Parameters: + # id: Id of the problem. + # Returns: none + # Author: Lin Kassem + def edit + @problem = AssignmentProblem.find_by_id(params[:id]) + if @problem.nil? + render "problem_not_found" + else + @assignment = @problem.assignment + @course = @assignment.course + @can_edit = @course.can_edit(current_lecturer) + @can_edit||= @course.can_edit(current_teaching_assistant) + end + end +end \ No newline at end of file diff --git a/tutor/app/controllers/assignments_controller.rb b/tutor/app/controllers/assignments_controller.rb new file mode 100644 index 00000000..1488d623 --- /dev/null +++ b/tutor/app/controllers/assignments_controller.rb @@ -0,0 +1,39 @@ +class AssignmentsController < ApplicationController + # [View List Of Assignments - Story 4.24] + # Shows a particular assignment in a course + # Parameters: + # id: Id of the assignment. + # Returns: + # The view of the assignmet if found, + # redirects to 'public/404' otherwise. + # Author: Lin Kassem + def show + id = params[:id] + @assignment = Assignment.find_by_id(id) + if @assignment + @course = @assignment.course + @problems = @assignment.problems + @can_edit = @course.can_edit(current_lecturer) + @can_edit||= @course.can_edit(current_teaching_assistant) + else + render ('public/404') + end + end + + # [View List Of Assignments - Story 4.24] + # Allows the editing of a particular assignment + # Parameters: + # id: Id of the assignment. + # Returns: none + # Author: Lin Kassem + def edit + @assignment = Assignment.find_by_id(params[:id]) + if @assignment.nil? + render "problem_not_found" + else + @course = @assignment.course + @can_edit = @course.can_edit(current_lecturer) + @can_edit||= @course.can_edit(current_teaching_assistant) + end + end +end \ No newline at end of file diff --git a/tutor/app/controllers/courses_controller.rb b/tutor/app/controllers/courses_controller.rb index 8881048e..0590b38f 100644 --- a/tutor/app/controllers/courses_controller.rb +++ b/tutor/app/controllers/courses_controller.rb @@ -37,6 +37,10 @@ def sign_up student = Student.find(current_student.id) if student.courses.find_by_id(@course.id) == nil student.courses << @course + @course.topics.each do |topic| + progress = TrackProgression.create(level: 0, topic_id: topic.id) + student.progressions << progress + end else @status = "7" end @@ -102,7 +106,7 @@ def new # none # Returns: # none - # Author: Mohamed Mamdouh + # Author: Mohamed Mamdouh + Ahmed Elassuty def create @new_course = Course.new @new_course.name = course_params[:name] @@ -110,6 +114,7 @@ def create @new_course.year = course_params[:year] @new_course.semester = course_params[:semester] @new_course.description = course_params[:description] + @new_course.link = course_params[:link] @new_course.university = current_lecturer.university if @new_course.save current_lecturer.courses << @new_course @@ -155,7 +160,14 @@ def show tracks = [] @topics.each do |t| tracks = tracks + t.tracks + end + @assignments = @course.assignments + assignment_problems = [] + @assignments.each do |a| + assignment_problems = assignment_problems + a.problems end + @can_edit = @course.can_edit(current_lecturer) + @can_edit||= @course.can_edit(current_teaching_assistant) else render ('public/404') end @@ -212,7 +224,7 @@ def share private def course_params - params.require(:course).permit(:name,:code,:year,:semester,:description) + params.require(:course).permit(:name,:code,:year,:semester,:description,:link) end # [Share Performance - Story 5.2, 5.13] diff --git a/tutor/app/controllers/debuggers_controller.rb b/tutor/app/controllers/debuggers_controller.rb index b3a78bae..d698dd9b 100644 --- a/tutor/app/controllers/debuggers_controller.rb +++ b/tutor/app/controllers/debuggers_controller.rb @@ -16,8 +16,10 @@ def start id = current_student.id pid = params[:id] code = params[:code] + problem_type = params[:problem_type] cases = if params[:case] then params[:case] else "" end - result = SolutionsLayer.debug "java", code, id, pid, cases + class_name = params[:class_name] + result = SolutionsLayer.debug "java", code, id, pid, problem_type, class_name, cases render json: result end diff --git a/tutor/app/controllers/notes_controller.rb b/tutor/app/controllers/notes_controller.rb new file mode 100644 index 00000000..086afb97 --- /dev/null +++ b/tutor/app/controllers/notes_controller.rb @@ -0,0 +1,72 @@ +class NotesController < ApplicationController + + # [Mark Assignment - 4.29] + # creates a new record to Note Table + # Parameters: + # content: note's content through note_params + # solution_id: the id of the solution that the note belongs to + # line: integer represents the order of the line that the note belongs to + # Returns: none + # Author: Abdullrahman Elhusseny + def create + note = Note.new(note_params) + if lecturer_signed_in? + note.owner_id = current_lecturer.id + note.owner_type = "lecturer" + elsif teaching_assistant_signed_in? + note.owner_id = current_teaching_assistant.id + note_type = "teaching assistant" + end + if note.save + flash.keep[:notice] = "Note added! Hold your cursor over line of code to see the note" + else + flash.keep[:notice] = "Note wasn't added" + end + redirect_to :back + end + + # [Mark Assignment - 4.29] + # updates a record to Note Table + # Parameters: + # content: note's content through note_params + # solution_id: the id of the solution that the note belongs to + # line: integer represents the order of the line that the note belongs to + # Returns: none + # Author: Abdullrahman Elhusseny + def update + @note = Note.find_by_id(params[:id]) + if @note.update_attributes(note_params) + flash.keep[:notice] = "Note Updated" + else + flash.keep[:notice] = "Note wasn't updated" + end + redirect_to :back + end + + # [Mark Assignment - 4.29] + # Removes a record to Note Table + # Parameters: + # id: the id of the note to be deleted through params + # Returns: none + # Author: Abdullrahman Elhusseny + def destroy + if Note.find_by_id(params[:id]).destroy + flash[:notice] = "Note Deleted" + redirect_to :back + end + end + + # [Mark Assignment - Story 4.29] + # passes the input of the form as paramaters for create & update action + # Parameters: + # content: note's content through note_params + # solution_id: the id of the solution that the note belongs to + # line: integer represents the order of the line that the note belongs to + # Returns: + # params to create action & update action + # Author: Abdullrahman Elhusseny + private + def note_params + params.require(:Note).permit(:content, :solution_id, :line) + end +end diff --git a/tutor/app/controllers/problems_controller.rb b/tutor/app/controllers/problems_controller.rb index dc839854..3d8c38d2 100644 --- a/tutor/app/controllers/problems_controller.rb +++ b/tutor/app/controllers/problems_controller.rb @@ -63,6 +63,7 @@ def edit @track = Track.find_by_id(@problem.track_id) @topic = Topic.find_by_id(@track.topic_id) @tracks = Track.where(topic_id: @track.topic_id) + @snippet = @problem.snippet else render ('public/404') end @@ -112,7 +113,7 @@ def destroy # problem_params: a problem's title, description & track_id # Returns: # Refreshes divisions in the page using AJAX without refreshing the whole page - # Author: Abdullrahman Elhusseny + # Author: Abdullrahman Elhusseny + Rami Khalil def update @problem = Problem.find_by_id(params[:id]) @track = Track.find_by_id(@problem.track_id) @@ -123,13 +124,16 @@ def update @message = "Description updated" elsif problem_params[:track_id].to_i != @problem.track_id @message = "Problem is moved to Track #{problem_params[:track_id]}" + elsif problem_params[:snippet] != @problem.snippet + @message = "Problem code template updated." else - flash.keep[:notice] = "You have entered the same paramater no change has been made!" + flash.keep[:notice] = "You have entered the same parameters. No change has been made!" end if problem_params[:track_id].to_i == @problem.track_id if problem_params[:title] != @problem.title || - problem_params[:description] != @problem.description + problem_params[:description] != @problem.description || + problem_params[:snippet] != @problem.snippet begin if @problem.update_attributes(problem_params) flash.keep[:notice] = @message @@ -204,16 +208,13 @@ def done # [Add Problem - 4.4] # Passes the input of the form as paramaters for create action to use it - # Parameters: - # title: problem's title - # description: problem's description - # track_id: problem's track id + # Parameters: none # Returns: - # Params to create action & update action - # Author: Abdullrahman Elhusseny + # Filtered parameter list for problems + # Author: Abdullrahman Elhusseny + Rami Khalil private def problem_params - params.require(:Problem).permit(:title, :description, :track_id) + params.require(:Problem).permit(:title, :description, :track_id, :snippet) end end \ No newline at end of file diff --git a/tutor/app/controllers/solutions_controller.rb b/tutor/app/controllers/solutions_controller.rb index 99e7cb36..5d562e68 100644 --- a/tutor/app/controllers/solutions_controller.rb +++ b/tutor/app/controllers/solutions_controller.rb @@ -13,7 +13,10 @@ def create student = current_student.id problem = param[:problem_id] time = param[:time] - result = SolutionsLayer.validate "java", code, student, problem, time + problem_type = param[:problem_type] + class_name = param[:class_name] + result = SolutionsLayer.validate("java", code, student, problem, + problem_type, class_name, time) render json: result end @@ -31,14 +34,16 @@ def execute id = current_student.id pid = params[:problem_id] code = params[:code] + problem_type = params[:problem_type] + class_name = params[:class_name] cases = if params[:input] then params[:input] else "" end - result = SolutionsLayer.execute "java", code, id, pid, cases + result = SolutionsLayer.execute("java", code, id, pid, + problem_type, class_name, cases) render json: result end # [Compiler: Compile - Story 3.4] # Creates a soution for the current problem in the database and compiles it. - # Then it places the previous code and the compilation results and feedback in the flash hash. # Parameters: # solution_params: submitted from the form_for # Returns: none @@ -48,10 +53,69 @@ def compile_solution code = param[:code] student = current_student.id problem = param[:problem_id] - compiler_feedback = SolutionsLayer.compile "java", code, student, problem + problem_type = param[:problem_type] + class_name = param[:class_name] + compiler_feedback = SolutionsLayer.compile("java", code, student, + problem, problem_type, class_name) render json: compiler_feedback end + # [Mark Solution - Story 4.29] + # Allows a TA to mark a solution of a student + # Parameters: + # submission_id: the id of the solution to be marked + # Returns: none + # Author: Abdullrahman Elhusseny + def mark_solution + if lecturer_signed_in? || teaching_assistant_signed_in? + @solution = Solution.find_by_id(params[:submission_id]) + if !@solution.blank? + @lines = @solution.code.split("\n") + @notes = Hash.new + @counter = 0 + @lines.each do |line| + @counter+= 1 + @notes[@counter]= Note.where(solution_id: @solution.id, line: @counter).last + end + @student = Student.find_by_id(@solution.student_id) + @problem = AssignmentProblem.find_by_id(@solution.problem_id) + @course = @problem.assignment.course + @can_edit = @course.can_edit(current_lecturer) + @can_edit||= @course.can_edit(current_teaching_assistant) + if !@can_edit + render ('public/404') + end + else + render ('public/404') + end + else + render ('public/404') + end + end + + # [Mark Solution - Story 4.29] + # Allows a TA to view all submitted solutions to a specific assignment + # Parameters: + # problem_id: the id of the assignment problem to view its submissions + # Returns: none + # Author: Abdullrahman Elhusseny + def view_submissions + @problem = AssignmentProblem.find_by_id(params[:problem_id]) + @submissions = @problem.solutions.group(:student_id) + @students = Hash.new + @counter = 0 + @submissions.each do |submission| + @counter+=1 + @students[@counter] = Student.find_by_id(submission.student_id) + end + @course = @problem.assignment.course + @can_edit = @course.can_edit(current_lecturer) + @can_edit||= @course.can_edit(current_teaching_assistant) + if !@can_edit + render ('public/404') + end + end + private # [Code Editor: Write Code - Story 3.3] # Permits the id of the problem, code from the form_for @@ -62,7 +126,7 @@ def compile_solution # none # Author: MOHAMEDSAEED def solution_params - params.permit(:code, :problem_id, :time) + params.permit(:code, :problem_id, :time, :class_name, :problem_type) end # [Compiler: Test - Story 3.15] diff --git a/tutor/app/controllers/students_controller.rb b/tutor/app/controllers/students_controller.rb index c7ca2dc0..75614bf4 100644 --- a/tutor/app/controllers/students_controller.rb +++ b/tutor/app/controllers/students_controller.rb @@ -14,9 +14,8 @@ def get_performance @solved = Attempt.where(student_id:params[:student_id], success:true).joins(problem: {track: :topic}).where('topics.course_id' => params[:course_id]).select("DISTINCT problem_id").count @failed = Attempt.where(student_id:params[:student_id], failure:true).joins(problem: {track: :topic}).where('topics.course_id' => params[:course_id]).select("DISTINCT problem_id").count @incomplete = Attempt.where(student_id:params[:student_id], incomplete:true).joins(problem: {track: :topic}).where('topics.course_id' => params[:course_id]).select("DISTINCT problem_id").count - #Recheck here for the missing DB - #@solved_contest = Attempt.where(student_id:params[:student_id], success:true).joins(contest_problem: {track: :topic}).where('topics.course_id' => params[:course_id]).select("DISTINCT problem_id").count - #@failed_contest = Attempt.where(student_id:params[:student_id], failure:true).joins(contest_problem: {track: :topic}).where('topics.course_id' => params[:course_id]).select("DISTINCT problem_id").count + @solved_contest = ContesProgress.where(student_id:params[:student_id], status:true).joins(:contest).where('contests.course_id' => params[:course_id]).select("DISTINCT cproblem_id").count + @solved_contest = ContesProgress.where(student_id:params[:student_id], status:false).joins(:contest).where('contests.course_id' => params[:course_id]).select("DISTINCT cproblem_id").count end def list_courses @@ -55,8 +54,37 @@ def incomplete_problems @failure_list = Attempt.where(student_id:params[:student_id], icnomplete:true).joins(problem: {track: :topic}).where('topics.course_id' => params[:course_id]).select("DISTINCT problem_id") end + # [Contest statistics - Story 5.30] + # This method retrieve variables from tables in the database + # Parameters: + # params: A hash of the request URL attributes + # Returns: + # An array of solved contest problems + # Author: Mahdi + def solved_contest_problems + @solved_contest_list = ContesProgress.where(student_id:params[:student_id], status:true).joins(:contest).where('contests.course_id' => params[:course_id]).select("DISTINCT cproblem_id") + end + + # [Performance of a student - Story 5.3] + # This method retrieve variables from tables in the database + # Parameters: + # params: A hash of the request URL attributes + # Returns: + # An array of failed problems + # Author: Mahdi + def failed_contest_problems + @failure_list = ContesProgress.where(student_id:params[:student_id], status:false).joins(:contest).where('contests.course_id' => params[:course_id]).select("DISTINCT cproblem_id") + end + + # [Contest Registrants - Story 5.27] + # This method retrieve variables from tables in the database + # Parameters: + # params: A hash of the request URL attributes + # Returns: + # An array of contest registrants + # Author: Mahdi def view_registrants - @contest_registrants = Contest.find_by_id(2).students + @contest_registrants = Contest.find_by_id(params[:id]).students end # [Profile - Story 5.8] diff --git a/tutor/app/controllers/topics_controller.rb b/tutor/app/controllers/topics_controller.rb index 8e37bba1..52f51af4 100644 --- a/tutor/app/controllers/topics_controller.rb +++ b/tutor/app/controllers/topics_controller.rb @@ -38,6 +38,22 @@ def new @new_topic = Topic.new end + # [Delete Topic - Story 1.34] + # This action takes the topic id, remove it from the database + # and then redirects the user to the show courses page accompanied + # with a "Topic deleted" message. + # Parameters: + # params[:id]: The current topic's id + # Returns: none + # Author: Mohamed Saeed + def destroy + topic = Topic.find_by_id(params[:id]) + topic.destroy + flash[:success_deletion] = "Topic deleted." + @course = topic.course + redirect_to :controller => 'courses', :action => 'show', :id => @course.id + end + # [Specify Topics - Story 1.2] # Description: This action takes the passed course id and assings # the respective topics of that course to an instance @@ -62,23 +78,24 @@ def index # topic_params[]: A list that has all fields entered by the user to in the # create_topic_form # Returns: - # flash[:notice]: A message indicating the success or failure of the creation - # Author: Ahmed Akram + # flash[:notice]: A message indicating the success or failure of the creation + # Author: Ahmed Akram + Mohamed Saeed def create @new_topic = Topic.new @new_topic.title = topic_params[:title] @new_topic.description = topic_params[:description] bool = @new_topic.save - if bool == true + if bool == true flash[:notice] = "Topic successfully created" @course = Course.find(course_params[:course_id]) @course.topics << @new_topic redirect_to :action => 'index' + Topic.update_track_progression @new_topic else if @new_topic.errors.any? flash[:notice] = @new_topic.errors.full_messages.first end - render :action => 'new' + render :action => 'new' end end diff --git a/tutor/app/controllers/utilities_controller.rb b/tutor/app/controllers/utilities_controller.rb index 2f31e5a1..df079999 100644 --- a/tutor/app/controllers/utilities_controller.rb +++ b/tutor/app/controllers/utilities_controller.rb @@ -7,9 +7,35 @@ class UtilitiesController < ApplicationController # Author: Ahmed Elassuty def simple_search @lecturers = Lecturer.simple_search(params[:search]) + .paginate(:page => params[:page], :per_page => 3) @students = Student.simple_search(params[:search]) + .paginate(:page => params[:page], :per_page => 3) @teaching_assisstants = TeachingAssistant.simple_search(params[:search]) + .paginate(:page => params[:page], :per_page => 3) @courses = Course.simple_search(params[:search]) + .paginate(:page => params[:page], :per_page => 3) + @objects = @courses + if @lecturers.count > @students.count + if @lecturers.count > @teaching_assisstants.count + if @lecturers.count > @courses.count + @objects = @lecturers + end + else + if @teaching_assisstants.count > @courses.count + @objects = @teaching_assistants + end + end + else + if @students.count > @teaching_assisstants.count + if @students.count > @courses.count + @objects = @students + end + else + if @teaching_assisstants.count > @courses.count + @objects = @teaching_assisstants + end + end + end respond_to do |format| format.js format.html @@ -52,11 +78,16 @@ def advanced_search # Returns: A hashes with search results according to the keyword # Author: Ahmed Elassuty def auto_complete - @objects = Lecturer.simple_search(params[:q]).take(1) - respond_to do |format| - format.json {render :template => 'utilities/auto_complete', - :formats => [], :handlers => [:json_builder], :layout=>false} - end + objects = Lecturer.simple_search(params[:term]).take(2) + auto_complete = [] + auto_complete = auto_complete.concat(objects.map(&:name)) unless objects.nil? + objects = Student.simple_search(params[:term]).take(2) + auto_complete = auto_complete.concat(objects.map(&:name)) unless objects.nil? + objects = TeachingAssistant.simple_search(params[:term]).take(2) + auto_complete = auto_complete.concat(objects.map(&:name)) unless objects.nil? + objects = Course.simple_search(params[:term]).take(2) + auto_complete = auto_complete.concat(objects.map(&:name)) unless objects.nil? + render json: auto_complete end -end +end \ No newline at end of file diff --git a/tutor/app/helpers/assignment_problems_helper.rb b/tutor/app/helpers/assignment_problems_helper.rb new file mode 100644 index 00000000..16025111 --- /dev/null +++ b/tutor/app/helpers/assignment_problems_helper.rb @@ -0,0 +1,2 @@ +module AssignmentProblemsHelper +end diff --git a/tutor/app/helpers/assignments_helper.rb b/tutor/app/helpers/assignments_helper.rb new file mode 100644 index 00000000..6f7c33b9 --- /dev/null +++ b/tutor/app/helpers/assignments_helper.rb @@ -0,0 +1,2 @@ +module AssignmentsHelper +end diff --git a/tutor/app/helpers/notes_helper.rb b/tutor/app/helpers/notes_helper.rb new file mode 100644 index 00000000..8078f730 --- /dev/null +++ b/tutor/app/helpers/notes_helper.rb @@ -0,0 +1,2 @@ +module NotesHelper +end diff --git a/tutor/app/models/admin.rb b/tutor/app/models/admin.rb deleted file mode 100644 index fe0bd288..00000000 --- a/tutor/app/models/admin.rb +++ /dev/null @@ -1,11 +0,0 @@ -class Admin < ActiveRecord::Base - - #Validations - - #Relations - - #Scoops - - #Methods - -end diff --git a/tutor/app/models/admin_user.rb b/tutor/app/models/admin_user.rb new file mode 100644 index 00000000..318ec397 --- /dev/null +++ b/tutor/app/models/admin_user.rb @@ -0,0 +1,6 @@ +class AdminUser < ActiveRecord::Base + # Include default devise modules. Others available are: + # :confirmable, :lockable, :timeoutable and :omniauthable + devise :database_authenticatable, + :recoverable, :rememberable, :trackable, :validatable +end diff --git a/tutor/app/models/assignment.rb b/tutor/app/models/assignment.rb index 826e990f..ffb88c4e 100644 --- a/tutor/app/models/assignment.rb +++ b/tutor/app/models/assignment.rb @@ -1,6 +1,7 @@ class Assignment < ActiveRecord::Base #Validations + validates :title, presence:true #Relations belongs_to :course diff --git a/tutor/app/models/assignment_problem.rb b/tutor/app/models/assignment_problem.rb index 903336ef..ad007c41 100644 --- a/tutor/app/models/assignment_problem.rb +++ b/tutor/app/models/assignment_problem.rb @@ -1,6 +1,7 @@ class AssignmentProblem < ActiveRecord::Base #Validations + validates :title, presence:true #Relations belongs_to :owner, polymorphic: true diff --git a/tutor/app/models/course.rb b/tutor/app/models/course.rb index 1207b51f..ecfd9993 100644 --- a/tutor/app/models/course.rb +++ b/tutor/app/models/course.rb @@ -5,7 +5,8 @@ class Course < ActiveRecord::Base include Tire::Model::Callbacks #Validations - validates :name, presence: true + validates :name, presence: true + validates :link, :format => /\Ahttps?:\/\/((w{3})[.])?([\w]|-)+(\.com|\.org|\.guc|\.edu|\.eg|\.gov|\.biz|\.info|\.net)+((\?|#|\/)([\S])*)?\z/ validates :description, presence: true validates :code, presence: true validates :year, presence: true, numericality: {only_integer: true, greater_than_or_equal_to: Date.today.year, diff --git a/tutor/app/models/java_compiler.rb b/tutor/app/models/java_compiler.rb index 9803be7d..0dec7ab3 100644 --- a/tutor/app/models/java_compiler.rb +++ b/tutor/app/models/java_compiler.rb @@ -1,10 +1,11 @@ -class JavaCompiler +class JavaCompiler < ActiveRecord::Base # Methods # [Compiler: Compile - Story 3.4] - # Writes the given code to a .java file with the name st[student_id]pr[problem_id]so[solution_id] - # using java_file_name/2. Then it compiles that file and returns the compiler's feedback. + # Writes the given code to a .java file in a folder with the name + # st[student_id]pr[problem_id]so[solution_id] using folder_name/0. + # Then it compiles that file and returns the compiler's feedback. # Parameters: # solution: The submitted solution. # code: The code to be compiled. @@ -12,26 +13,25 @@ class JavaCompiler # The compiler's feedback. # Author: Ahmed Moataz def self.compile(solution, code) - %x[ #{'mkdir -p ' + Solution::JAVA_PATH} ] - file_name = solution.java_file_name(true, true) - File.open(file_name, 'w') { |file| file.write(code) } - %x[ #{'mkdir -p ' + Solution::CLASS_PATH} ] - return %x[ #{'javac -g -d ' + Solution::CLASS_PATH + ' ' + file_name + ' 2>&1'} ] + return solution.check_class_name if solution.check_class_name != "" + folder_name = solution.folder_name + file_path = solution.file_path + %x[ #{'mkdir -p ' + Solution::SOLUTION_PATH + folder_name} ] + File.open(file_path, 'w') { |file| file.write(code) } + return %x[ #{'javac -g ' + file_path + ' 2>&1'} ] end # [Compiler: Compile - Story 3.4] - # Adds the class enclosure to the submitted code using append_class/4. - # Then compiles it using compile/4 and passes its result and feedback in a list. + # Compiles the solution's code using compile/2 + # and returns its result and feedback in a list. # Parameters: # solution: The submitted solution. # Returns: - # A list. The first element is a boolean indicating the result of the compilation process. - # The second element contains the compilation errors if any. - # The third element contains the compiled code. + # A list. The first element is a boolean indicating the result of the + # compilation process. The second element contains the compilation errors if any. # Author: Ahmed Moataz def self.compiler_feedback(solution) - new_code = append_class(solution) - feedback = compile(solution, new_code) + feedback = compile(solution, solution.code) if feedback == "" return {success: true, errors: nil} else @@ -41,29 +41,16 @@ def self.compiler_feedback(solution) end # [Compiler: Compile - Story 3.4] - # Appends the class enclosure to the to submitted code. - # Parameters: - # solution: The submitted solution. - # Returns: - # The code with the class enclosure. - # Author: Ahmed Moataz - def self.append_class(solution) - name = solution.file_name - code = solution.code - return 'public class ' + name + " {\n" + code - end - - # [Compiler: Compile - Story 3.4] - # Changes the error headers to CoolSoft. + # Changes the error headers to the solution's class name. # Parameters: # solution: The submitted solution. # feedback: The compiler's feedback. # Returns: - # The compiler's feedback with the error headers changed to CoolSoft. + # The compiler's feedback with the error headers changed to the solution's class name. # Author: Ahmed Moataz def self.change_error_headers(solution, feedback) - header = solution.file_name - return feedback.gsub('students_solutions/Java/' + header, 'CoolSoft').gsub(header, 'CoolSoft') + header = Solution::SOLUTION_PATH + solution.folder_name + solution.class_name + return feedback.gsub(header, solution.class_name).gsub(header, solution.class_name) end end \ No newline at end of file diff --git a/tutor/app/models/java_debugger.rb b/tutor/app/models/java_debugger.rb index 3a7e19fb..ebb632fb 100644 --- a/tutor/app/models/java_debugger.rb +++ b/tutor/app/models/java_debugger.rb @@ -1,4 +1,5 @@ require "open3" +require "fileutils" class JavaDebugger #Methods @@ -60,33 +61,32 @@ def input(input) # input : The arguments to be passed to the main method # Returns: A List of all 100 steps ahead # Authors: Mussab ElDash + Rami Khalil - def start(class_name, input) + def start(class_name, input, time = 10) $all = [] - source_path = "#{Rails.root.to_s}/#{Solution::JAVA_PATH}" - Dir.chdir(Solution::CLASS_PATH){ - begin - $input, $output, $error, $wait_thread = Open3.popen3("jdb", - "-sourcepath", source_path, class_name, *input) - buffer_until_ready - input "stop in #{class_name}.main" - buffer_until_ready - input "run" - nums = get_line - locals = get_variables - nums[:locals] = locals - $all << nums + status = "The debugging session was successful." + begin + $input, $output, $error, $wait_thread = Open3.popen3("jdb", class_name, *input) + buffer_until_ready + input "stop in #{class_name}.main" + buffer_until_ready + input "run" + nums = get_line + locals = get_variables + nums[:locals] = locals + $all << nums + status = TimeLimit.start(time) { debug - rescue => e - unless e.message === 'Exited' - return false - end + } + rescue => e + unless e.message === 'Exited' + return false end - } + end begin Process.kill("TERM", $wait_thread.pid) rescue => e end - return $all + return $all, status end # [Debugger: Debug - Story 3.6] @@ -210,13 +210,15 @@ def get_exception # Author: Mussab ElDash def self.debug(solution, input) debugger = JavaDebugger.new - class_name = solution.file_name - debugging = debugger.start(class_name, input.split(" ")) - java_file = solution.java_file_name true, true - class_file = solution.class_file_name true, true - File.delete(java_file) - File.delete(class_file) - return {:success => true, data: debugging} + class_name = solution.class_name + folder_name = Solution::SOLUTION_PATH + solution.folder_name + debugging = "" + status = "" + Dir.chdir(folder_name) { + debugging, status = debugger.start(class_name, input.split(" ")) + } + FileUtils.rm_rf(folder_name) + return {:success => true, data: debugging, status: status} end # [Debugger: View Variables - Story 3.7] diff --git a/tutor/app/models/java_executer.rb b/tutor/app/models/java_executer.rb index 7bff6e38..6b466bcd 100644 --- a/tutor/app/models/java_executer.rb +++ b/tutor/app/models/java_executer.rb @@ -10,11 +10,13 @@ class JavaExecuter # Author: Ahmed Akram def self.execute(sol, input) @solution = sol - class_path = Solution::CLASS_PATH - file_name = @solution.file_name + file_name = @solution.class_name + folder_name = @solution.folder_name validity = check_input_validity(input, @solution.problem.id) if validity[:status] - @execute_res = %x[#{'java -cp ' + class_path + ' ' + file_name + ' ' + input + ' 2>&1'}] + Dir.chdir(Solution::SOLUTION_PATH + folder_name) { + @execute_res = %x[#{'java ' + file_name + ' ' + input + ' 2>&1'}] + } if @execute_res.include?("Exception") return {executer_feedback: false, executer_output: get_runtime_error()} else diff --git a/tutor/app/models/lecturer.rb b/tutor/app/models/lecturer.rb index 6dbe6b7f..bc25ac1c 100644 --- a/tutor/app/models/lecturer.rb +++ b/tutor/app/models/lecturer.rb @@ -53,6 +53,8 @@ class Lecturer < ActiveRecord::Base has_many :grades, as: :editor has_many :resources, as: :owner + has_many :notes, as: :owner + has_many :notifications, as: :receiver #Methods # [Advanced Search - Story 1.23] diff --git a/tutor/app/models/note.rb b/tutor/app/models/note.rb new file mode 100644 index 00000000..9610e2a6 --- /dev/null +++ b/tutor/app/models/note.rb @@ -0,0 +1,13 @@ +class Note < ActiveRecord::Base + + #Validations + validates_presence_of :content + validates_presence_of :solution_id + validates_presence_of :line + #Relations + belongs_to :owner, polymorphic: true + belongs_to :problems + + #Methods + +end diff --git a/tutor/app/models/notification.rb b/tutor/app/models/notification.rb new file mode 100644 index 00000000..348f58bd --- /dev/null +++ b/tutor/app/models/notification.rb @@ -0,0 +1,12 @@ +class Notification < ActiveRecord::Base + + #Validations + + #Relations + belongs_to :receiver, polymorphic: true + + #Scoops + + #Methods + +end diff --git a/tutor/app/models/solution.rb b/tutor/app/models/solution.rb index 77b56eb3..275a1ff7 100644 --- a/tutor/app/models/solution.rb +++ b/tutor/app/models/solution.rb @@ -9,6 +9,8 @@ class Solution < ActiveRecord::Base belongs_to :assignment_problem, class_name:"AssignmentProblem", polymorphic: true belongs_to :contest_problem, class_name:"Cproblem", polymorphic: true + has_many :notes, dependent: :destroy + #Methods # [Compiler: Validate - Story 3.5] # Checks the validity of a submitted solution @@ -21,41 +23,36 @@ class Solution < ActiveRecord::Base # Author: MOHAMEDSAEED def self.validate(solution, test_cases) response = [] - compiler_status = JavaCompiler.compiler_feedback(solution) - if compiler_status[:success] - test_cases.each do |testcase| - input = testcase.input - expected_output = testcase.output - runtime_check = JavaExecuter.execute(solution, input) - if(runtime_check[:executer_feedback]) - output = runtime_check[:executer_output][:message] - if (output != expected_output) - if(output.to_s.empty?) - output = "Empty" - end - response << {success: false, test_case: input, - response: "Logic error: Expected output: " + - expected_output.to_s.strip + ", but your output was: " + output} - unless solution.status == 4 - solution.status = 5 - end - else - response << {success: true, test_case: input, - response: "Passed!"} - unless solution.status == 4 | 5 - solution.status = 1 - end + test_cases.each do |testcase| + input = testcase.input + expected_output = testcase.output + runtime_check = JavaExecuter.execute(solution, input) + if(runtime_check[:executer_feedback]) + output = runtime_check[:executer_output][:message] + if (output != expected_output) + if(output.to_s.empty?) + output = "Empty" end - else - runtime_error = runtime_check[:executer_output] - explanation = get_response(runtime_error) - solution.status = 4 response << {success: false, test_case: input, - response: "Runtime error: " + explanation} + response: "Logic error: Expected output: " + + expected_output.to_s.strip + ", but your output was: " + output} + unless solution.status == 4 + solution.status = 5 + end + else + response << {success: true, test_case: input, + response: "Passed!"} + unless solution.status == 4 | 5 + solution.status = 1 + end end + else + runtime_error = runtime_check[:executer_output] + explanation = get_response(runtime_error) + solution.status = 4 + response << {success: false, test_case: input, + response: "Runtime error: " + explanation} end - else - return {compiler_error: true, compiler_output: compiler_status} end solution.save return response @@ -154,6 +151,50 @@ def class_file_name(prepend_path = false, append_extension = false) return jfile_name end + # [Compiler: Compile - Story 3.4] + # Returns the folder's name, which the solution's files will be placed into. + # Parameters: none + # Returns: + # The folder's name. + # Author: Ahmed Moataz + def folder_name + return 'st' + student_id.to_s + 'pr' + problem_id.to_s + 'so' + id.to_s + '/' + end + + # [Compiler: Compile - Story 3.4] + # Returns the file path of the solution's files. + # Parameters: + # append_extension: A boolean value indicating if the file extension + # should be appended or not. + # Returns: + # path: The file path. + # Author: Ahmed Moataz + def file_path(append_extension = true) + path = SOLUTION_PATH + folder_name + class_name + path += '.java' if append_extension + return path + end + + # [Compiler: Compile - Story 3.4] + # Checks if the class_name contains special characters, line breaks, or white spaces. + # Parameters: none + # Returns: + # A String containing feedback on what it found. + # Author: Ahmed Moataz + def check_class_name + special = "?<>',?[]}{=-)(*&|^%$#`~{}/\\:;" + regex = /[#{special.gsub(/./){|char| "\\#{char}"}}]/ + if class_name =~ regex + return "The file name cannot contain special characters" + elsif class_name.include?("\n") + return "The file name cannot contain line breaks" + elsif class_name.include?(" ") || class_name.include?("\t") + return "The file name cannot contain white spaces" + else + return "" + end + end + #Constants STATUS_SUBMITTED = 0 STATUS_ACCEPTED = 1 @@ -163,5 +204,6 @@ def class_file_name(prepend_path = false, append_extension = false) STATUS_EXECUTED_WITH_LOGIC_ERRORS = 5 JAVA_PATH = 'students_solutions/Java/' CLASS_PATH = 'students_solutions/Class/' + SOLUTION_PATH = 'students_solutions/' end \ No newline at end of file diff --git a/tutor/app/models/solutions_layer.rb b/tutor/app/models/solutions_layer.rb index 5afb0691..c67ffe0e 100644 --- a/tutor/app/models/solutions_layer.rb +++ b/tutor/app/models/solutions_layer.rb @@ -11,13 +11,13 @@ class SolutionsLayer # Returns: # A hash with the status of the execution # Author: Mussab ElDash - def self.execute lang, code, student_id, problem_id, cases + def self.execute lang, code, student_id, problem_id, problem_type, class_name, cases executer = get_executer lang unless executer return false end compiler = get_compiler lang - solution = get_solution code, student_id, problem_id + solution = get_solution code, student_id, problem_id, problem_type, class_name compile_status = {} if compiler compile_status = compiler.compiler_feedback(solution) @@ -38,9 +38,9 @@ def self.execute lang, code, student_id, problem_id, cases # Returns: # The compile status # Author: Mussab ElDash - def self.compile lang, code, student_id, problem_id + def self.compile lang, code, student_id, problem_id, problem_type, class_name compiler = get_compiler lang - solution = get_solution code, student_id, problem_id + solution = get_solution code, student_id, problem_id, problem_type, class_name if compiler feed_back = compiler.compiler_feedback solution if feed_back[:success] @@ -65,8 +65,8 @@ def self.compile lang, code, student_id, problem_id # Returns: # A hash with the validation status # Author: Mussab ElDash - def self.validate lang, code, student_id, problem_id, time - solution = get_solution code, student_id, problem_id + def self.validate lang, code, student_id, problem_id, problem_type, class_name, time + solution = get_solution code, student_id, problem_id, problem_type, class_name solution.time = time test_cases = solution.problem.test_cases compiler = get_compiler lang @@ -90,8 +90,8 @@ def self.validate lang, code, student_id, problem_id, time # Returns: # A hash with the debugging results # Author: Mussab ElDash - def self.debug lang, code, student_id, problem_id, cases - solution = get_solution code, student_id, problem_id + def self.debug lang, code, student_id, problem_id, problem_type, class_name, cases + solution = get_solution code, student_id, problem_id, problem_type, class_name debugger = get_debugger lang compiler = get_compiler lang if debugger @@ -116,9 +116,10 @@ def self.debug lang, code, student_id, problem_id, cases # Returns: # A new Solution # Author: Mussab ElDash - def self.get_solution code, student_id, problem_id + def self.get_solution code, student_id, problem_id, type = "Problem", class_name + type = type.capitalize solution = Solution.create({code: code, student_id: student_id, - problem_id: problem_id}) + problem_id: problem_id, problem_type: type, class_name: class_name}) return solution end diff --git a/tutor/app/models/student.rb b/tutor/app/models/student.rb index 2e24dcbb..e49ab39d 100644 --- a/tutor/app/models/student.rb +++ b/tutor/app/models/student.rb @@ -47,6 +47,7 @@ class Student < ActiveRecord::Base has_and_belongs_to_many :contests, class_name:"Contest", join_table: "contests_students" has_many :grades + has_many :notifications, as: :receiver #Methods diff --git a/tutor/app/models/teaching_assistant.rb b/tutor/app/models/teaching_assistant.rb index 0a39a462..05609df5 100644 --- a/tutor/app/models/teaching_assistant.rb +++ b/tutor/app/models/teaching_assistant.rb @@ -55,6 +55,8 @@ class TeachingAssistant < ActiveRecord::Base has_many :grades, as: :editor has_many :resources, as: :owner + has_many :notes, as: :owner + has_many :notifications, as: :receiver #Methods # [Advanced Search - Story 1.23] diff --git a/tutor/app/models/time_limit.rb b/tutor/app/models/time_limit.rb new file mode 100644 index 00000000..a765c15e --- /dev/null +++ b/tutor/app/models/time_limit.rb @@ -0,0 +1,48 @@ +require 'timeout' +require 'open3' +class TimeLimit + + # [Terminate - Story X.9] + # Run the system call given and terminate it or Terminate the given block + # after the time given is reached while running + # Parameters: + # time: The time for the program not to exceed + # args: The system call to be executed + # Returns: + # In case there is an args passes it will return the output and error streams lines + # else none + # Author: Mussab ElDash + def self.start time, args = "" + unless args.empty? + stdin, stdout, stderr, wait_thr = Open3.popen3 args + pid = wait_thr.pid + terminated = false + begin + status = Timeout::timeout(time) { + Process.wait(pid) + } + rescue => e + end + stdin.close + begin + Process.kill("TERM", pid) + rescue => e + terminated = true + end + result_out = stdout.read + result_err = stderr.read + return terminated, result_out, result_err + else + begin + Timeout::timeout(time) { + yield + } + rescue => e + unless e.message === 'Exited' + return "The debugging session timed out." + end + end + return "The debugging session was successful." + end + end +end \ No newline at end of file diff --git a/tutor/app/models/topic.rb b/tutor/app/models/topic.rb index b232b441..2db3f746 100644 --- a/tutor/app/models/topic.rb +++ b/tutor/app/models/topic.rb @@ -42,4 +42,20 @@ def self.search(params) end end end + + # [Course Sign-Up - Story 2.6] + # Update the the progress of all students for a topic in a + # specfic course + # Parameters: + # Topic: the topic in which the progress of students should be updated + # Returns: none + # Author: Mohamed Saeed + def self.update_track_progression topic + course = Course.find_by_id(topic.course_id) + course.students.each do |student| + progress = TrackProgression.create(level: 0, topic_id: topic.id) + student.progressions << progress + end + end + end diff --git a/tutor/app/models/track.rb b/tutor/app/models/track.rb index 40badeaf..419f0d15 100644 --- a/tutor/app/models/track.rb +++ b/tutor/app/models/track.rb @@ -13,4 +13,49 @@ class Track < ActiveRecord::Base #Scoops #Methods + + # [Create Track - Story 4.1] + # Get a text difficulty of this track + # Parameters: none + # Returns: + # A string according to the current difficulty of this track + # Author: Mussab ElDash + def get_difficulty_name + if difficulty == 0 + return "Very Easy" + elsif difficulty == 1 + return "Easy" + elsif difficulty == 2 + return "Medium" + elsif difficulty == 3 + return "Hard" + elsif difficulty == 4 + return "Very Hard" + else + return "Not Supported" + end + end + + # [Create Track - Story 4.1] + # Get a text difficulty of the number given + # Parameters: + # difficulty: The difficulty that be converted to string and by default its -1 + # Returns: + # A string according to the number given + # Author: Mussab ElDash + def self.get_difficulty_name difficulty = -1 + if difficulty == 0 + return "Very Easy" + elsif difficulty == 1 + return "Easy" + elsif difficulty == 2 + return "Medium" + elsif difficulty == 3 + return "Hard" + elsif difficulty == 4 + return "Very Hard" + else + return "Not Supported" + end + end end diff --git a/tutor/app/views/assignment_problems/edit.html.erb b/tutor/app/views/assignment_problems/edit.html.erb new file mode 100644 index 00000000..6b7f10e8 --- /dev/null +++ b/tutor/app/views/assignment_problems/edit.html.erb @@ -0,0 +1 @@ +

Edit assignment problem Page

\ No newline at end of file diff --git a/tutor/app/views/assignment_problems/show.html.erb b/tutor/app/views/assignment_problems/show.html.erb new file mode 100644 index 00000000..04ab13a0 --- /dev/null +++ b/tutor/app/views/assignment_problems/show.html.erb @@ -0,0 +1,25 @@ +\ +\ + +

+
+
+
+ + + + +
+
+ <% if @can_edit %> + <%= link_to "Edit problem", { :controller => 'assignment_problems', + :action => 'edit', :id => @problem.id }, class: "btn btn-info" %> +
+ <%= render partial: "solutions/new" %> +
+ <% end %> + <% if !@can_edit %> + <%= render partial: "solutions/new" %> + <% end %> +
+
\ No newline at end of file diff --git a/tutor/app/views/assignments/edit.html.erb b/tutor/app/views/assignments/edit.html.erb new file mode 100644 index 00000000..07405e76 --- /dev/null +++ b/tutor/app/views/assignments/edit.html.erb @@ -0,0 +1 @@ +

Edit Assignment Page

\ No newline at end of file diff --git a/tutor/app/views/assignments/show.html.erb b/tutor/app/views/assignments/show.html.erb new file mode 100644 index 00000000..a48ff9bf --- /dev/null +++ b/tutor/app/views/assignments/show.html.erb @@ -0,0 +1,46 @@ + +
+
+

Assignment title: <%= @assignment.title %>

+

Submission: <%= @assignment.due_date.day %>. + <%= @assignment.due_date.month %>. + <%= @assignment.due_date.year %>

+

+<% if @assignment.description != nil %> +
+

Assignment description:

     + <%= @assignment.description %>

+
+<% end %> +
+
+

List of problems: + <% if @problems.length == 0 %> +

+

    No problems added yet!

+ <% else %> + + <% end %> +

+
+

+<% if @can_edit %> + <%= link_to "Edit assignment", { :controller => 'assignments', + :action => 'edit', :id => @assignment.id }, class: "btn btn-info" %> +<% end %> \ No newline at end of file diff --git a/tutor/app/views/courses/edit.html.erb b/tutor/app/views/courses/edit.html.erb index a6a15853..aae70eed 100644 --- a/tutor/app/views/courses/edit.html.erb +++ b/tutor/app/views/courses/edit.html.erb @@ -69,6 +69,11 @@ <%= c.label :semester %>
<%= c.text_area :semester %>

+ +

+ <%= c.label :link %>
+ <%= c.url_field :link %> +

<%= c.label :description %>
diff --git a/tutor/app/views/courses/new.html.erb b/tutor/app/views/courses/new.html.erb index 17a44fab..0371c4bf 100644 --- a/tutor/app/views/courses/new.html.erb +++ b/tutor/app/views/courses/new.html.erb @@ -39,6 +39,17 @@ <%= f.text_area :description, class:"form-control", style:"width:300px", placeholder:"Description", :value => @new_course.description %>

+

+ <%= f.label :link %>
+ <% unless @new_course.errors[:link].blank? %> + + Link <%= @new_course.errors[:link].join(", ") %> + +
+ <% end %> + <%= f.url_field :link, class:"form-control", style:"width:300px", + placeholder:"Course Link", :value => @new_course.link %> +

<%= f.label :Semester %>
<% unless @new_course.errors[:semester].blank? %> diff --git a/tutor/app/views/courses/show.html.erb b/tutor/app/views/courses/show.html.erb index cee52fe7..c77b4720 100644 --- a/tutor/app/views/courses/show.html.erb +++ b/tutor/app/views/courses/show.html.erb @@ -11,6 +11,7 @@

<%= @course.name %>

+

Course Link

@@ -37,12 +38,12 @@ <%= @course.description %>

-
<%= link_to "View Discussion Board", { :controller => 'discussion_boards', :action => 'show', :id => @course.id }, class: "btn btn-default", method: :get, style: "text-decoration:none; margin-top: -70px" %>
+ <%= flash[:success_deletion] %> <% openable = true %>
<% @topics.each do |t| %> @@ -63,10 +64,16 @@ <%= link_to t.title, t, style: 'color:#003366;' %> <%= t.tracks.count %> + <% if lecturer_signed_in? || teaching_assistant_signed_in? %> + <%= link_to "Delete Topic", {controller: "topics", + action: "destroy", :id => t.id}, + style: 'float: right', + class: "btn btn-danger btn-xs", method: :delete %> + <% end %>
    - <% t.tracks.each do |tt|%> + <% t.tracks.each do |tt| %> <% color = "label label-default" %>
  • @@ -78,6 +85,58 @@
<% end %>
+ + <% openable = true %> +
+ <% if openable %> + <% open = "in" %> + <% openarrow = "glyphicon-chevron-down" %> + <% openable = false %> + <% else %> + <% open = "" %> + <% openarrow = "glyphicon-chevron-right" %> + <% end %> +
+
+ Assignments + + <%= @course.assignments.count %> +
+
+
    + <% if @can_edit %> + <% @course.assignments.each do |a| %> + <% color = "label label-default" %> +
  • + <%= link_to a.title, a, class: color %> + <% if @can_edit %> + <% if !a.publish %> + unpublished +
  • + <% end %> + <% end %> + <% end %> + <% else %> + <% @course.assignments.each do |a| %> + <% if a.publish %> + <% color = "label label-default" %> +
  • + <%= link_to a.title, a, class: color %> + <% if a.due_date > DateTime.now.to_date %> + Due +
  • + <% end %> + <% end %> + <% end %> + <% end %> +
+
+
+
diff --git a/tutor/app/views/hints/_give.html.erb b/tutor/app/views/hints/_give.html.erb index 3b24c229..e69de29b 100644 --- a/tutor/app/views/hints/_give.html.erb +++ b/tutor/app/views/hints/_give.html.erb @@ -1,13 +0,0 @@ -<% - hint_count = 0; - @problem.model_answers.each do |model_answer| - model_answer.hints.where(category: false).each do |hint| - %> - - <% - hint_count = hint_count + 1 - end - end -%> \ No newline at end of file diff --git a/tutor/app/views/layouts/_search_form.html.erb b/tutor/app/views/layouts/_search_form.html.erb index ad51c574..352cc291 100644 --- a/tutor/app/views/layouts/_search_form.html.erb +++ b/tutor/app/views/layouts/_search_form.html.erb @@ -4,10 +4,11 @@ # Author: Ahmed Elassuty --> <%= form_tag utilities_simple_search_path, :method => 'get', - remote: controller?("utilities","simple_search"), class: "navbar-form navbar-left" do %> + remote: controller?("utilities","simple_search"), class: "navbar-form navbar-left" do %>
<%= text_field_tag :search, params[:search], class: "form-control col-lg-8", - placeholder: "Search for Users and Courses" %> + placeholder: "Search for Users and Courses", id: "search_field", + data: { autocomplete_source: utilities_auto_complete_path } %> <%= link_to "Try Advanced", utilities_advanced_search_path, class: "btn btn-default col-lg-12" %> diff --git a/tutor/app/views/layouts/application.html.erb b/tutor/app/views/layouts/application.html.erb index 05577bd2..71cd5db5 100644 --- a/tutor/app/views/layouts/application.html.erb +++ b/tutor/app/views/layouts/application.html.erb @@ -4,14 +4,11 @@ # Author: Ahmed Elassuty --> - + Tutor - <%= stylesheet_link_tag "application", "bootstrap", :media => "all" %> - - <%-# stylesheet_link_tag "application", "bootstrap", "token-input", :media => "all" -%> - <%= stylesheet_link_tag "application", "bootstrap", :media => "all" %> - <%= javascript_include_tag "application", "bootstrap", "jquery" %> + <%= stylesheet_link_tag "application", :media => "all" %> + <%= javascript_include_tag "application" %> <%= csrf_meta_tags %> @@ -23,9 +20,9 @@ - CoolSoft - Java Tutor + CoolSoft - Java Tutor
- -
+
<% if signed_in? %>
@@ -64,7 +62,7 @@

diff --git a/tutor/app/views/problems/_description.html.erb b/tutor/app/views/problems/_description.html.erb index ec0815c6..b97b29d5 100644 --- a/tutor/app/views/problems/_description.html.erb +++ b/tutor/app/views/problems/_description.html.erb @@ -4,6 +4,7 @@

<%= problem.submit('update description') %>

+ <%= problem.hidden_field :snippet, value: @problem.snippet %> <%= problem.hidden_field :title, value: @problem.title %> <%= problem.hidden_field :track_id, value: @problem.track_id %> <% end %> \ No newline at end of file diff --git a/tutor/app/views/problems/_snippet.html.erb b/tutor/app/views/problems/_snippet.html.erb new file mode 100644 index 00000000..e89bca67 --- /dev/null +++ b/tutor/app/views/problems/_snippet.html.erb @@ -0,0 +1,10 @@ +

<%= @problem.snippet %>

+<%= form_for :Problem, url: problem_path(@problem), method: :patch, remote: true do |problem| %> + <%= problem.text_area :snippet, cols: 50, rows: 10 %> +

+ <%= problem.submit('Update code snippet') %> +

+ <%= problem.hidden_field :description, value: @problem.description %> + <%= problem.hidden_field :title, value: @problem.title %> + <%= problem.hidden_field :track_id, value: @problem.track_id %> +<% end %> \ No newline at end of file diff --git a/tutor/app/views/problems/_title.html.erb b/tutor/app/views/problems/_title.html.erb index a6059318..8d79eaaa 100644 --- a/tutor/app/views/problems/_title.html.erb +++ b/tutor/app/views/problems/_title.html.erb @@ -2,6 +2,7 @@ <%= form_for :Problem, url: problem_path(@problem), method: :patch, remote: true do |problem| %> <%= problem.text_field :title %> <%= problem.submit('update title') %> + <%= problem.hidden_field :snippet, value: @problem.snippet %> <%= problem.hidden_field :description, value: @problem.description %> <%= problem.hidden_field :track_id, value: @problem.track_id %> <% end %> \ No newline at end of file diff --git a/tutor/app/views/problems/_track.html.erb b/tutor/app/views/problems/_track.html.erb index 7438dc76..532299af 100644 --- a/tutor/app/views/problems/_track.html.erb +++ b/tutor/app/views/problems/_track.html.erb @@ -3,6 +3,7 @@ <% if @tracks.size > 1 %> <%= problem.collection_select(:track_id, @tracks, :id, :title) %> <%= problem.submit('Change Track') %> + <%= problem.hidden_field :snippet, value: @problem.snippet %> <%= problem.hidden_field :title, value: @problem.title %> <%= problem.hidden_field :description, value: @problem.description %> <% end %> diff --git a/tutor/app/views/problems/edit.html.erb b/tutor/app/views/problems/edit.html.erb index 77d41093..997778fb 100644 --- a/tutor/app/views/problems/edit.html.erb +++ b/tutor/app/views/problems/edit.html.erb @@ -9,6 +9,7 @@ <%= form_for :Problem, url: problem_path(@problem), method: :patch, remote: true do |problem| %> <%= problem.text_field :title %> <%= problem.submit('update title') %> + <%= problem.hidden_field :snippet, value: @problem.snippet %> <%= problem.hidden_field :description, value: @problem.description %> <%= problem.hidden_field :track_id, value: @problem.track_id %> <% end %> @@ -21,6 +22,21 @@

<%= problem.submit('update description') %>

+ <%= problem.hidden_field :snippet, value: @problem.snippet %> + <%= problem.hidden_field :title, value: @problem.title %> + <%= problem.hidden_field :track_id, value: @problem.track_id %> + <% end %> +
+ +
+

<%= @problem.snippet %>

+ <%= form_for :Problem, url: problem_path(@problem), + method: :patch, remote: true do |problem| %> + <%= problem.text_area :snippet, cols: 50, rows: 10 %> +

+ <%= problem.submit('Update code snippet') %> +

+ <%= problem.hidden_field :description, value: @problem.description %> <%= problem.hidden_field :title, value: @problem.title %> <%= problem.hidden_field :track_id, value: @problem.track_id %> <% end %> @@ -32,6 +48,7 @@ <% if @tracks.size > 1 %> <%= problem.collection_select(:track_id, @tracks, :id, :title) %> <%= problem.submit('Change Track') %> + <%= problem.hidden_field :snippet, value: @problem.snippet %> <%= problem.hidden_field :title, value: @problem.title %> <%= problem.hidden_field :description, value: @problem.description %> <% end %> diff --git a/tutor/app/views/problems/new.html.erb b/tutor/app/views/problems/new.html.erb index 7c2f3598..cb90ca7c 100644 --- a/tutor/app/views/problems/new.html.erb +++ b/tutor/app/views/problems/new.html.erb @@ -15,6 +15,13 @@ <%= problem.text_area :description, :cols => "50", :rows => "10", class: "form-control", style: "width:60%" %>

+

+ <%= problem.label :snippet %>
+ <%= problem.text_area :snippet, cols: 50, rows: 10, + class: "form-control", style: "width: 60%", + value: "public class CoolSoft {\n\t + public static void main(String [] args) {\n\t\t\n\t}\n}" %> +

<%= problem.hidden_field :track_id, value: params[:id] %>

<%= problem.submit "Save Problem", class: "btn btn-info" %> diff --git a/tutor/app/views/problems/show.html.erb b/tutor/app/views/problems/show.html.erb index c74f86a8..ad28a51f 100644 --- a/tutor/app/views/problems/show.html.erb +++ b/tutor/app/views/problems/show.html.erb @@ -24,6 +24,7 @@ <%= render partial: "hints/give" %>

Tips:

+ <%= render partial: "tips/give" %>

<% end %> diff --git a/tutor/app/views/problems/update.js.erb b/tutor/app/views/problems/update.js.erb index e8138844..1f3df37c 100644 --- a/tutor/app/views/problems/update.js.erb +++ b/tutor/app/views/problems/update.js.erb @@ -1,4 +1,5 @@ $('#div1').html("<%= escape_javascript(render 'problems/flash') %>"); $('#div2').html("<%= escape_javascript(render 'problems/title') %>"); $('#div3').html("<%= escape_javascript(render 'problems/description') %>"); -$('#div4').html("<%= escape_javascript(render 'problems/track') %>"); \ No newline at end of file +$('#div4').html("<%= escape_javascript(render 'problems/track') %>"); +$('#div5').html("<%= escape_javascript(render 'problems/snippet') %>"); \ No newline at end of file diff --git a/tutor/app/views/solutions/_new.html.erb b/tutor/app/views/solutions/_new.html.erb index 94ceed63..75f937dd 100644 --- a/tutor/app/views/solutions/_new.html.erb +++ b/tutor/app/views/solutions/_new.html.erb @@ -1,36 +1,60 @@ Time spent: 0:00 -<%= form_for :solution, - :url => { :controller => "solutions", :action => "create" }, - :html => {:method => :post} do |f| %> -<%= - @code = f.hidden_field "code", :id =>'solution_code', - value: flash[:previous_code] == nil ? - "\tpublic static void main(String [] args) {\n\t\t\n\t}\n}" - : flash[:previous_code].to_s -%> -<%= f.hidden_field :problem_id, value: params[:id] %> -
-<%= button_tag 'Reload Template', type: 'Button', id: 'reloadButton', - onClick: "reload_template()", class: "btn btn-info" %> -
-
+

+ +
+ + Class Name: + + + + + + + + <%= button_tag 'Reload Template', type: 'Button', id: 'reloadButton', + onClick: "reload_template()", class: "btn btn-info pull-right" %> +
-
-
-<%= f.hidden_field :problem_id, value: params[:id] %> -
+
public class CoolSoft {
+	public static void main(String [] args) {
+		
+	}
+}
+



<%= button_tag 'Compile', type: 'Button',id: 'compileButton', - onClick: "compile(#{params[:id]})", class: "btn btn-info" %> + onClick: "compile(#{params[:id]},'Problem')", class: "btn btn-info" %> <%= button_tag 'Run Test Case', class: "btn btn-info", id: 'testButton', - type: 'Button', onClick: "run_input(#{params[:id]})" %> + type: 'Button', onClick: "run_input(#{params[:id]},'Problem')" %> <%= button_tag 'Start Debugging', id: 'debugButton', type: 'button', - onclick: "start_debug(#{params[:id]})", class: "btn btn-info" %> + onclick: "start_debug(#{params[:id]},'Problem')", class: "btn btn-info" %> <%= button_tag 'Previous', type: 'Button', class: "btn btn-info", style: "display: none", id: 'previousButton', onClick: 'previous()', hidden: true %> @@ -48,18 +72,18 @@ Runtime Error Compilation Error <%= button_tag 'Submit', type: 'Button',id: 'submitButton', - onClick: "validate_code(#{params[:id]})", class: "btn btn-info" %> + onClick: "validate_code(#{params[:id]},'Problem')", class: "btn btn-info" %>
-

+
- Input:
- <%= f.text_area :solution_input, placeholder: "Write your test case here", + Input:
+ <%= text_area :solution_input, placeholder: "Write your test case here", id: "solution_input", class: "form-control", style: "width:400px" %>
-
+
Console:
@@ -103,7 +127,7 @@ editor.setTheme("ace/theme/twilight"); edit_session.setMode("ace/mode/java"); - edit_session.setValue(code_header + hidden_code_field.value); + edit_session.setValue(code_header + hidden_code_field.value + code_footer); var code_filter_function = function(event_handler) { var unfiltered_code = edit_session.getValue(); @@ -113,4 +137,3 @@ }; edit_session.on('change', code_filter_function); -<% end %> \ No newline at end of file diff --git a/tutor/app/views/solutions/mark_solution.html.erb b/tutor/app/views/solutions/mark_solution.html.erb new file mode 100644 index 00000000..694959dd --- /dev/null +++ b/tutor/app/views/solutions/mark_solution.html.erb @@ -0,0 +1,90 @@ +<% if flash[:notice] %> +
<%= flash[:notice] %>
+<% end %> + + + + + + + + <% @counter = 0 %> + <% @lines.each do |line| %> + <% @counter+= 1 %> + <% @note = @notes[@counter] %> + + + + + + <% if @note %> +
+ <%= form_for :Note, url: note_path(@note), + method: :patch do |note| %> +

+ <%= note.text_field :content, + class: "form-control", + style: "width:60%", + value: @note.content %> +

+ <%= note.hidden_field :solution_id, + value: @solution.id %> + <%= note.hidden_field :line, + value: @counter %> +

+ <%= note.submit "Update Note", class: "btn btn-info" %> +

+ <% end %> + <%= button_to "Delete Note",{:controller => 'notes', + :action => 'destroy', + :id => @note.id}, method: :delete, + class: "btn btn-info" %> +
+ <% else %> +
+ <%= form_for :Note, url: {controller: "notes", + action: "create"} do |note| %> +

+ <%= note.text_field :content, + class: "form-control", + style: "width:60%", + placeholder: "Write your note here!" %> +

+ <%= note.hidden_field :solution_id, + value: @solution.id %> + <%= note.hidden_field :line, + value: @counter %> +

+ <%= note.submit "Save Note", class: "btn btn-info" %> +

+ <% end %> +
+ <% end %> + <% end %> + +

Line

Code

+

Note Action

<%= @counter %> +
+						<% end %>" style = "font-weight:bold;"><%= line %>
+
+

+ <% if @note %> + <%= image_tag "edit_button.png", + id: "#{@counter}edit_button", + class: "edit_button", + onclick: "pop(this.id);" %> + <% else %> + <%= image_tag "note.png", + id: "#{@counter}add_button", + class: "add_note_button", + onclick: "pop(this.id);" %> + <% end %> +

+
+


+<%= link_to "I'm done marking", + {:action => 'view_submissions', :problem_id => @problem.id}, + class: "btn btn-info" %> \ No newline at end of file diff --git a/tutor/app/views/solutions/view_submissions.html.erb b/tutor/app/views/solutions/view_submissions.html.erb new file mode 100644 index 00000000..1c182693 --- /dev/null +++ b/tutor/app/views/solutions/view_submissions.html.erb @@ -0,0 +1,48 @@ +<% if @submissions.empty? %> +

No solutions have been submitted to this problem

+<% else %> + + + + + + + + + + + <% @counter = 0 %> + <% @submissions.each do |submission| %> + <% @counter+= 1 %> + + <% @student = @students[@counter] %> + + + + + + + + + + + <% end %> +
StudentProblemSubmission TimePassed test cases
<%= @student.name %><%= @problem.title %><%= submission.created_at %> + <% if submission.status == 0 %> + <%= image_tag "delete_button.png", + class: "x_button", + title: "Solution didn't pass test cases!" %> + <% elsif submission.status == 1 %> + <%= image_tag "tick.png", + class: "tick_button", + title: "Solution passed test cases!" %> + <% end %> + <%= link_to "Mark and grade", + {:action => 'mark_solution', + :submission_id => submission.id}, + class: "btn btn-info" %>
+<% end %> +


+<%= link_to "Back to assignment page",{:controller => 'assignments', + :action => 'show', :id => @problem.assignment.id }, + :method => :get, class: "btn btn-info" %> \ No newline at end of file diff --git a/tutor/app/views/students/failed_contest_problems.html.erb b/tutor/app/views/students/failed_contest_problems.html.erb new file mode 100644 index 00000000..b395efe8 --- /dev/null +++ b/tutor/app/views/students/failed_contest_problems.html.erb @@ -0,0 +1,4 @@ +Failed Problems :-
+<% @failed_contest_list.each do |failed| %> + Problem: <%= failed.cproblem_id %>
+<% end %> \ No newline at end of file diff --git a/tutor/app/views/students/failed_problems.html.erb b/tutor/app/views/students/failed_problems.html.erb index 3da31454..5c212986 100644 --- a/tutor/app/views/students/failed_problems.html.erb +++ b/tutor/app/views/students/failed_problems.html.erb @@ -1,6 +1,4 @@ Failed Problems :-
<% @failure_list.each do |failed| %> Problem: <%= failed.problem_id %>
-<% end %> - -<%= debug(params) %> \ No newline at end of file +<% end %> \ No newline at end of file diff --git a/tutor/app/views/students/get_performance.html.erb b/tutor/app/views/students/get_performance.html.erb index bcefa484..0e5b514b 100644 --- a/tutor/app/views/students/get_performance.html.erb +++ b/tutor/app/views/students/get_performance.html.erb @@ -7,9 +7,15 @@ ["Wrong ones", @failed_contest]]%> <%= column_chart data_exc, library: {bar: {groupWidth: "10%"}}, - colors: ["lightblue", "grey", "lightgrey"]%> - + colors: ["lightblue"]%> <%= button_to "Solved Ones", {:controller => "students", :action => "solved_problems", :student_id => params[:student_id], :course_id => params[:course_id]}, :class => 'btn btn-success' %> <%= button_to "Wrong Ones", {:controller => "students", :action => "failed_problems", :student_id => params[:student_id], :course_id => params[:course_id]}, :class => 'btn btn-success' %> -<%= button_to "Incomplete Ones", {:controller => "students", :action => "incomplete_problems", :student_id => params[:student_id], :course_id => params[:course_id]}, :class => 'btn btn-success' %> \ No newline at end of file +<%= button_to "Incomplete Ones", {:controller => "students", :action => "incomplete_problems", :student_id => params[:student_id], :course_id => params[:course_id]}, :class => 'btn btn-success' %> + +<%= column_chart data_contest, + library: {bar: {groupWidth: "7%"}}, + colors: ["grey"]%> + +<%= button_to "Solved Ones", {:controller => "students", :action => "solved_contest_problems", :student_id => params[:student_id], :course_id => params[:course_id]}, :class => 'btn btn-success' %> +<%= button_to "Wrong Ones", {:controller => "students", :action => "failed_contest_problems", :student_id => params[:student_id], :course_id => params[:course_id]}, :class => 'btn btn-success' %> diff --git a/tutor/app/views/students/list_courses.html.erb b/tutor/app/views/students/list_courses.html.erb index 59c91dd5..176dfd56 100644 --- a/tutor/app/views/students/list_courses.html.erb +++ b/tutor/app/views/students/list_courses.html.erb @@ -1,6 +1,4 @@

Choose a course :-


<% @courses_list.each do |crs| %>
  • <%= link_to (Course.find_by_id(crs.course_id).name), students_get_performance_path(:student_id => crs.student_id, :course_id => crs.course_id) %>

  • -<% end %> - -<%= debug(@courses_list) %> \ No newline at end of file +<% end %> \ No newline at end of file diff --git a/tutor/app/views/students/solved_contest_problems.html.erb b/tutor/app/views/students/solved_contest_problems.html.erb new file mode 100644 index 00000000..a3caa436 --- /dev/null +++ b/tutor/app/views/students/solved_contest_problems.html.erb @@ -0,0 +1,4 @@ +Solved Problems :-
    +<% @solved_contest_list.each do |solved| %> + Problem: <%= solved.cproblem_id %>
    +<% end %> \ No newline at end of file diff --git a/tutor/app/views/students/solved_problems.html.erb b/tutor/app/views/students/solved_problems.html.erb index 65851456..5b3419a8 100644 --- a/tutor/app/views/students/solved_problems.html.erb +++ b/tutor/app/views/students/solved_problems.html.erb @@ -2,5 +2,3 @@ Solved Problems :-
    <% @solved_list.each do |solved| %> Problem: <%= solved.problem_id %>
    <% end %> - -<%=debug(params)%> diff --git a/tutor/app/views/students/view_registrants.html.erb b/tutor/app/views/students/view_registrants.html.erb index 70990ef6..c6a5b1c6 100644 --- a/tutor/app/views/students/view_registrants.html.erb +++ b/tutor/app/views/students/view_registrants.html.erb @@ -1,4 +1,4 @@ -

    Contest Name:-

    <%= Contest.find_by_id(2).title %> +

    Contest Name:-

    <%= Contest.find_by_id(params[:id]).title %>

    Registrants:-

    <% @contest_registrants.each do |registrant| %>
  • <%= registrant.name %>
  • diff --git a/tutor/app/views/tips/_give.html.erb b/tutor/app/views/tips/_give.html.erb new file mode 100644 index 00000000..2bbc5437 --- /dev/null +++ b/tutor/app/views/tips/_give.html.erb @@ -0,0 +1,13 @@ +<% + tip_count = 0; + @problem.model_answers.each do |model_answer| + model_answer.hints.where(category: true).each do |tip| + %> + + <% + tip_count = tip_count + 1 + end + end +%> \ No newline at end of file diff --git a/tutor/app/views/topics/show.html.erb b/tutor/app/views/topics/show.html.erb index 6287bcef..86d30294 100644 --- a/tutor/app/views/topics/show.html.erb +++ b/tutor/app/views/topics/show.html.erb @@ -17,9 +17,11 @@ <% end %> <% end %> +<% hash = (0..4).collect {|n| [n, (Track.get_difficulty_name n)]} %> +<% hash = hash.to_h %> <% openable = true %>
    - <% @tracks.each do |t| %> + <% @tracks.each do |track| %> <% if openable %> <% open = "in" %> <% openarrow = "glyphicon-chevron-down" %> @@ -30,23 +32,25 @@ <% end %>
    - - <%= link_to t.title , t , style: 'color:#003366;'%> - <%= t.problems.count %> + <%= link_to track.title , track , style: 'color:#003366;'%> + <% hash.delete track.difficulty %> + <%= track.problems.count %>
    -
    +
      - <% t.problems.each do |p|%> + <% track.problems.each do |problem|%> <% color = "" %> - <% if student_signed_in? && p.is_solved_by_student(current_student.id) %> + <% if student_signed_in? && + problem.is_solved_by_student(current_student.id) %> <% color = "label label-success" %> <% end %>
    • - <%= link_to p.title , p , class: color %> + data-placement="top" title="<%= problem.description%>"> + <%= link_to problem.title , problem , class: color %>
    • <% end %>
    @@ -58,29 +62,28 @@ <% if @can_edit %> - - New Edit Track Rating - - <% if !flash[:title] && !flash[:difficulty] %> - <% hide = 'hidden=true' %> - <% end %> -
    > - <%= form_for :Track , url: {controller: "tracks" , action: "create"}, - html: {class: "form-inline form-horizontal nifty_form" , role: "form"} do |f| %> -
    + <% if hash.count > 0 %> + New + <% if !flash[:title] && !flash[:difficulty] %> + <% hide = 'hidden=true' %> + <% end %> +

    +
    > + <%= form_for :Track , url: {controller: "tracks", action: "create"}, + html: {class: "form-inline form-horizontal nifty_form", role: "form"} do |f| %> <%= f.text_field :title , :value => flash[:title] , - :class => "form-control", :style => "width:150px", :placeholder => "Title"%>

    - <%= f.number_field :difficulty , :value => flash[:difficulty] , - :class => "form-control" , :placeholder => "Difficulty" , - :min => 1, :max => 5, :style => "width:150px" %> + :class => "form-control", :style => "width:150px", + :placeholder => "Title"%>

    + <%= f.select :difficulty, + options_for_select(hash.collect{|key,value| [value, key]}) %>

    <%= f.hidden_field :topic_id , :value => params[:id] %> <%= f.submit "Create" , :class => "btn btn-info"%> -
    - <% end %> -
    + <% end %> +
    + <% end %> <% end %> diff --git a/tutor/app/views/utilities/_result_form.html.erb b/tutor/app/views/utilities/_result_form.html.erb index 1e89335a..2e4f98b2 100644 --- a/tutor/app/views/utilities/_result_form.html.erb +++ b/tutor/app/views/utilities/_result_form.html.erb @@ -1,27 +1,15 @@ -<% if openable %> - <% open = "in" %> - <% openarrow = "glyphicon-chevron-down" %> -<% else %> - <% open = "" %> - <% openarrow = "glyphicon-chevron-right" %> -<% end %> <% if objects.present? %>
    -
    - - - - <%= objects.first.class.name.pluralize.to_s %> - - - <%= objects.count %> - + -
    +
      <% objects.each do |object| %>
    -<%end%> +<% end %> \ No newline at end of file diff --git a/tutor/app/views/utilities/_simple_search.html.erb b/tutor/app/views/utilities/_simple_search.html.erb index e5b03226..e9ba6a66 100644 --- a/tutor/app/views/utilities/_simple_search.html.erb +++ b/tutor/app/views/utilities/_simple_search.html.erb @@ -7,32 +7,30 @@

    No Result match

    <% else %> - <% openable = true %> <% if @lecturers.present? %>
    - <%= render "result_form", objects: @lecturers, openable: openable %> - <% openable = false %> + <%= render "result_form", objects: @lecturers %>
    <% end %> <% if @students.present? %>
    - <%= render "result_form", objects: @students, openable: openable %> - <% openable = false %> + <%= render "result_form", objects: @students %>
    <% end %> <% if @teaching_assisstants.present? %>
    - <%= render "result_form", objects: @teaching_assisstants, openable: openable %> - <% openable = false %> + <%= render "result_form", objects: @teaching_assisstants %>
    <% end %> <% if @courses.present? %>
    - <%= render "result_form", objects: @courses, openable: openable %> - <% openable = false %> + <%= render "result_form", objects: @courses %>
    <% end %> <% end %> +
    \ No newline at end of file diff --git a/tutor/app/views/utilities/advanced_search.html.erb b/tutor/app/views/utilities/advanced_search.html.erb index 2d6af627..b945e037 100644 --- a/tutor/app/views/utilities/advanced_search.html.erb +++ b/tutor/app/views/utilities/advanced_search.html.erb @@ -1,6 +1,12 @@ -Advanced Search Form + +
    +
    <%= render "advanced_search" %>
    -
    -
    \ No newline at end of file +
    \ No newline at end of file diff --git a/tutor/app/views/utilities/auto_complete.json_builder b/tutor/app/views/utilities/auto_complete.json_builder deleted file mode 100644 index ff619bcb..00000000 --- a/tutor/app/views/utilities/auto_complete.json_builder +++ /dev/null @@ -1,4 +0,0 @@ -array @objects.each do |object| - id object - name object.name -end \ No newline at end of file diff --git a/tutor/config/environment.rb b/tutor/config/environment.rb index e0489645..00e60e20 100644 --- a/tutor/config/environment.rb +++ b/tutor/config/environment.rb @@ -3,3 +3,6 @@ # Initialize the Rails application. Tutor::Application.initialize! + +#paginate +require 'will_paginate' \ No newline at end of file diff --git a/tutor/config/initializers/active_admin.rb b/tutor/config/initializers/active_admin.rb new file mode 100644 index 00000000..33d1d2a9 --- /dev/null +++ b/tutor/config/initializers/active_admin.rb @@ -0,0 +1,243 @@ +ActiveAdmin.setup do |config| + + # == Site Title + # + # Set the title that is displayed on the main layout + # for each of the active admin pages. + # + config.site_title = "Tutor" + + # Set the link url for the title. For example, to take + # users to your main site. Defaults to no link. + # + # config.site_title_link = "/" + + # Set an optional image to be displayed for the header + # instead of a string (overrides :site_title) + # + # Note: Aim for an image that's 21px high so it fits in the header. + # + # config.site_title_image = "logo.png" + + # == Default Namespace + # + # Set the default namespace each administration resource + # will be added to. + # + # eg: + # config.default_namespace = :hello_world + # + # This will create resources in the HelloWorld module and + # will namespace routes to /hello_world/* + # + # To set no namespace by default, use: + # config.default_namespace = false + # + # Default: + # config.default_namespace = :admin + # + # You can customize the settings for each namespace by using + # a namespace block. For example, to change the site title + # within a namespace: + # + # config.namespace :admin do |admin| + # admin.site_title = "Custom Admin Title" + # end + # + # This will ONLY change the title for the admin section. Other + # namespaces will continue to use the main "site_title" configuration. + + # == User Authentication + # + # Active Admin will automatically call an authentication + # method in a before filter of all controller actions to + # ensure that there is a currently logged in admin user. + # + # This setting changes the method which Active Admin calls + # within the application controller. + config.authentication_method = :authenticate_admin_user! + + # == User Authorization + # + # Active Admin will automatically call an authorization + # method in a before filter of all controller actions to + # ensure that there is a user with proper rights. You can use + # CanCanAdapter or make your own. Please refer to documentation. + # config.authorization_adapter = ActiveAdmin::CanCanAdapter + + # You can customize your CanCan Ability class name here. + # config.cancan_ability_class = "Ability" + + # You can specify a method to be called on unauthorized access. + # This is necessary in order to prevent a redirect loop which happens + # because, by default, user gets redirected to Dashboard. If user + # doesn't have access to Dashboard, he'll end up in a redirect loop. + # Method provided here should be defined in application_controller.rb. + # config.on_unauthorized_access = :access_denied + + # == Current User + # + # Active Admin will associate actions with the current + # user performing them. + # + # This setting changes the method which Active Admin calls + # (within the application controller) to return the currently logged in user. + config.current_user_method = :current_admin_user + + + # == Logging Out + # + # Active Admin displays a logout link on each screen. These + # settings configure the location and method used for the link. + # + # This setting changes the path where the link points to. If it's + # a string, the strings is used as the path. If it's a Symbol, we + # will call the method to return the path. + # + # Default: + config.logout_link_path = :destroy_admin_user_session_path + + # This setting changes the http method used when rendering the + # link. For example :get, :delete, :put, etc.. + # + # Default: + # config.logout_link_method = :get + + + # == Root + # + # Set the action to call for the root path. You can set different + # roots for each namespace. + # + # Default: + # config.root_to = 'dashboard#index' + + + # == Admin Comments + # + # This allows your users to comment on any resource registered with Active Admin. + # + # You can completely disable comments: + # config.allow_comments = false + # + # You can disable the menu item for the comments index page: + # config.show_comments_in_menu = false + # + # You can change the name under which comments are registered: + # config.comments_registration_name = 'AdminComment' + + + # == Batch Actions + # + # Enable and disable Batch Actions + # + config.batch_actions = true + + + # == Controller Filters + # + # You can add before, after and around filters to all of your + # Active Admin resources and pages from here. + # + # config.before_filter :do_something_awesome + + + # == Setting a Favicon + # + # config.favicon = '/assets/favicon.ico' + + + # == Removing Breadcrumbs + # + # Breadcrumbs are enabled by default. You can customize them for individual + # resources or you can disable them globally from here. + # + # config.breadcrumb = false + + + # == Register Stylesheets & Javascripts + # + # We recommend using the built in Active Admin layout and loading + # up your own stylesheets / javascripts to customize the look + # and feel. + # + # To load a stylesheet: + # config.register_stylesheet 'my_stylesheet.css' + # + # You can provide an options hash for more control, which is passed along to stylesheet_link_tag(): + # config.register_stylesheet 'my_print_stylesheet.css', :media => :print + # + # To load a javascript file: + # config.register_javascript 'my_javascript.js' + + + # == CSV options + # + # Set the CSV builder separator + # config.csv_options = { :col_sep => ';' } + # + # Force the use of quotes + # config.csv_options = { :force_quotes => true } + + + # == Menu System + # + # You can add a navigation menu to be used in your application, or configure a provided menu + # + # To change the default utility navigation to show a link to your website & a logout btn + # + # config.namespace :admin do |admin| + # admin.build_menu :utility_navigation do |menu| + # menu.add label: "My Great Website", url: "http://www.mygreatwebsite.com", html_options: { target: :blank } + # admin.add_logout_button_to_menu menu + # end + # end + # + # If you wanted to add a static menu item to the default menu provided: + # + # config.namespace :admin do |admin| + # admin.build_menu :default do |menu| + # menu.add label: "My Great Website", url: "http://www.mygreatwebsite.com", html_options: { target: :blank } + # end + # end + + + # == Download Links + # + # You can disable download links on resource listing pages, + # or customize the formats shown per namespace/globally + # + # To disable/customize for the :admin namespace: + # + # config.namespace :admin do |admin| + # + # # Disable the links entirely + # admin.download_links = false + # + # # Only show XML & PDF options + # admin.download_links = [:xml, :pdf] + # + # # Enable/disable the links based on block + # # (for example, with cancan) + # admin.download_links = proc { can?(:view_download_links) } + # + # end + + + # == Pagination + # + # Pagination is enabled by default for all resources. + # You can control the default per page count for all resources here. + # + # config.default_per_page = 30 + + + # == Filters + # + # By default the index screen includes a “Filters” sidebar on the right + # hand side with a filter for each attribute of the registered model. + # You can enable or disable them for all resources here. + # + # config.filters = true + +end diff --git a/tutor/config/routes.rb b/tutor/config/routes.rb index 9fd7ce20..d95df07b 100644 --- a/tutor/config/routes.rb +++ b/tutor/config/routes.rb @@ -1,5 +1,7 @@ Tutor::Application.routes.draw do - + + devise_for :admin_users, ActiveAdmin::Devise.config + ActiveAdmin.routes(self) devise_for :teaching_assistants devise_for :students devise_for :lecturers @@ -15,6 +17,8 @@ get "utilities/auto_complete" get 'courses/sign_up' get 'tracks/show_classmates/:id' => 'tracks#show_classmates' + get 'solutions/mark_solution' + get 'solutions/view_submissions' post 'solutions/compile_solution' => 'solutions#compile_solution' post 'courses/new' => 'courses#new' post 'courses/share' => 'courses#share' @@ -27,6 +31,8 @@ post 'students/solved_problems' => 'students#solved_problems' post 'students/failed_problems' => 'students#failed_problems' post 'students/incomplete_problems' => 'students#incomplete_problems' + post 'students/solved_contest_problems' => 'students#solved_contest_problems' + post 'students/failed_contest_problems' => 'students#failed_contest_problems' get 'students/view_registrants' => 'students#view_registrants' get 'problems/edit' @@ -36,8 +42,9 @@ get "tips/show" get "tips/index" get "tips/edit" - get "tips/destroy" + get "tips/destroy" post "tips/:id/edit" => 'tips#update' + get "notes/destroy" # You can have the root of your site routed with "root" root 'site#index' @@ -94,6 +101,9 @@ resources :posts resources :facebook resources :tips + resources :assignments + resources :assignment_problems + resources :notes # Example resource route with options: # resources :products do diff --git a/tutor/db/schema.rb b/tutor/db/schema.rb index b6f5f788..268584aa 100644 --- a/tutor/db/schema.rb +++ b/tutor/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20140509205739) do +ActiveRecord::Schema.define(version: 20140515223322) do create_table "acknowledgements", force: true do |t| t.string "message" @@ -22,15 +22,39 @@ t.datetime "updated_at" end - create_table "admins", force: true do |t| - t.string "name" - t.date "dob" - t.string "profile_image" - t.boolean "gender" + create_table "active_admin_comments", force: true do |t| + t.string "namespace" + t.text "body" + t.string "resource_id", null: false + t.string "resource_type", null: false + t.integer "author_id" + t.string "author_type" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "active_admin_comments", ["author_type", "author_id"], name: "index_active_admin_comments_on_author_type_and_author_id" + add_index "active_admin_comments", ["namespace"], name: "index_active_admin_comments_on_namespace" + add_index "active_admin_comments", ["resource_type", "resource_id"], name: "index_active_admin_comments_on_resource_type_and_resource_id" + + create_table "admin_users", force: true do |t| + t.string "email", default: "", null: false + t.string "encrypted_password", default: "", null: false + t.string "reset_password_token" + t.datetime "reset_password_sent_at" + t.datetime "remember_created_at" + t.integer "sign_in_count", default: 0, null: false + t.datetime "current_sign_in_at" + t.datetime "last_sign_in_at" + t.string "current_sign_in_ip" + t.string "last_sign_in_ip" t.datetime "created_at" t.datetime "updated_at" end + add_index "admin_users", ["email"], name: "index_admin_users_on_email", unique: true + add_index "admin_users", ["reset_password_token"], name: "index_admin_users_on_reset_password_token", unique: true + create_table "assignment_problems", force: true do |t| t.string "title" t.text "description" @@ -82,6 +106,7 @@ t.integer "contest_id" t.integer "student_id" t.integer "cproblem_id" + t.integer "trials" t.boolean "status" t.datetime "created_at" t.datetime "updated_at" @@ -93,10 +118,8 @@ t.string "title" t.text "description" t.boolean "incomplete" - t.time "start_time" - t.time "end_time" - t.date "start_date" - t.date "end_date" + t.datetime "start_time" + t.datetime "end_time" t.integer "course_id" t.integer "owner_id" t.string "owner_type" @@ -270,6 +293,16 @@ t.datetime "updated_at" end + create_table "notes", force: true do |t| + t.string "content" + t.integer "line" + t.integer "solution_id" + t.integer "owner_id" + t.string "owner_type" + t.datetime "created_at" + t.datetime "updated_at" + end + create_table "notification_mails", force: true do |t| t.string "subject" t.string "email" @@ -278,6 +311,15 @@ t.datetime "updated_at" end + create_table "notifications", force: true do |t| + t.string "message", null: false + t.boolean "seen", default: false + t.integer "receiver_id" + t.string "receiver_type" + t.datetime "created_at" + t.datetime "updated_at" + end + create_table "posts", force: true do |t| t.string "title" t.string "img" @@ -303,13 +345,14 @@ create_table "problems", force: true do |t| t.string "title" t.text "description" - t.boolean "incomplete" - t.integer "views_count" - t.integer "time_limit" + t.text "snippet" + t.integer "views_count", default: 0 + t.integer "time_limit", default: 0 t.integer "track_id" - t.string "snippet" - t.boolean "fill_gaps" - t.boolean "public" + t.boolean "fill_gaps", default: false + t.boolean "incomplete", default: true + t.boolean "seen", default: false + t.boolean "duplicated", default: false t.integer "owner_id" t.string "owner_type" t.datetime "created_at" @@ -352,9 +395,9 @@ create_table "solutions", force: true do |t| t.text "code" - t.integer "length" + t.integer "length", default: 0 t.integer "status" - t.integer "time" + t.integer "time", default: 0 t.text "class_name" t.integer "student_id" t.integer "problem_id" @@ -372,16 +415,16 @@ t.string "faculty" t.string "major" t.integer "semester" - t.boolean "advising" - t.boolean "probation" + t.boolean "advising", default: false + t.boolean "probation", default: false t.datetime "created_at" t.datetime "updated_at" - t.string "email", default: "", null: false - t.string "encrypted_password", default: "", null: false + t.string "email", default: "", null: false + t.string "encrypted_password", default: "", null: false t.string "reset_password_token" t.datetime "reset_password_sent_at" t.datetime "remember_created_at" - t.integer "sign_in_count", default: 0, null: false + t.integer "sign_in_count", default: 0, null: false t.datetime "current_sign_in_at" t.datetime "last_sign_in_at" t.string "current_sign_in_ip" @@ -443,6 +486,8 @@ t.string "output" t.integer "model_answer_id" t.integer "problem_id" + t.integer "cproblem_id" + t.integer "assignment_problem_id" t.integer "owner_id" t.string "owner_type" t.datetime "created_at" @@ -472,7 +517,7 @@ create_table "tracks", force: true do |t| t.string "title" t.integer "difficulty" - t.integer "views_count" + t.integer "views_count", default: 0 t.integer "topic_id" t.integer "owner_id" t.string "owner_type" diff --git a/tutor/db/seeds.rb b/tutor/db/seeds.rb index 33a5e768..3534b730 100644 --- a/tutor/db/seeds.rb +++ b/tutor/db/seeds.rb @@ -10,7 +10,6 @@ puts("**************************************************************") puts("# ----------------------- Admins ----------------------- ") - Admin.create(name: "Admin") puts("# ----------------------- Lecturers ----------------------- ") l = Lecturer.new(email: '1@lecturer.com', password: '123456789', @@ -67,19 +66,19 @@ puts("# ----------------------- Courses ----------------------- ") Course.create(name:"Data Structures and Alogrithms", - description:"This is a very easy course", code:"CSEN1", year:2014, semester:1) + description:"This is a very easy course", code:"CSEN1", year:2014, semester:1, link: "http://met.guc.edu.eg/Courses/CourseEdition.aspx?crsEdId=487") Course.create(name:"Computer Programming Lab", - description:"This course's evaluation system is the bad", code:2, year:2014, semester:1) + description:"This course's evaluation system is the bad", code:2, year:2014, semester:1, link: "http://met.guc.edu.eg/Courses/CourseEdition.aspx?crsEdId=487") Course.create(name:"Course3", - description:"This is course three", code:3, year:2014, semester:1) + description:"This is course three", code:3, year:2014, semester:1, link: "http://met.guc.edu.eg/Courses/CourseEdition.aspx?crsEdId=487") Course.create(name: "CS 2", - description:"This is course four", code: "cs2", year: 2014, semester: 2, university: "GUC") + description:"This is course four", code: "cs2", year: 2014, semester: 2, university: "GUC", link: "http://met.guc.edu.eg/Courses/CourseEdition.aspx?crsEdId=487") Course.create(name: "CS 3", - description:"This is course five", code: "cs3", year: 2014, semester: 3, university: "GUC") + description:"This is course five", code: "cs3", year: 2014, semester: 3, university: "GUC", link: "http://met.guc.edu.eg/Courses/CourseEdition.aspx?crsEdId=487") Course.create(name: "CS 4", - description:"This is course six", code: "cs4", year: 2014, semester: 4, university: "AUC") + description:"This is course six", code: "cs4", year: 2014, semester: 4, university: "AUC", link: "http://met.guc.edu.eg/Courses/CourseEdition.aspx?crsEdId=487") Course.create(name: "CS 5", - description:"This is course seven", code: "cs5", year: 2014, semester: 5, university: "AUC") + description:"This is course seven", code: "cs5", year: 2014, semester: 5, university: "AUC", link: "http://met.guc.edu.eg/Courses/CourseEdition.aspx?crsEdId=487") puts("# ----------------------- Course_Student ----------------------- ") CourseStudent.create(share: true) @@ -183,16 +182,27 @@ Cproblem.create(title: "ContestProblem 5", description: "This will be very easy Problem") Cproblem.create(title: "ContestProblem 6", description: "This is very easy Problem") ## Exercise + Problem.create(title: "Problem 1", description: "Given two numbers a and b, output a/b", - incomplete: false) + incomplete: false, + snippet: "public class CoolSoft {\n\tpublic static void main(String [] args) + {\n\t\t\n\t}\n}") Problem.create(title: "Problem 2", description: "This is very hard Problem", - incomplete: false) + incomplete: false, + snippet: "public class CoolSoft {\n\tpublic static void main(String [] args) + {\n\t\t\n\t}\n}") Problem.create(title: "Problem 3", description: "This wont be a hard Problem", - incomplete: false) + incomplete: false, + snippet: "public class CoolSoft {\n\tpublic static void main(String [] args) + {\n\t\t\n\t}\n}") Problem.create(title: "Problem 4", description: "This will be very easy Problem", - incomplete: true) + incomplete: true, + snippet: "public class CoolSoft {\n\tpublic static void main(String [] args) + {\n\t\t\n\t}\n}") Problem.create(title: "Problem 5", description: "This is very easy Problem", - incomplete: true) + incomplete: true, + snippet: "public class CoolSoft {\n\tpublic static void main(String [] args) + {\n\t\t\n\t}\n}") puts("# ----------------------- Tracks ----------------------- ") Track.create(title: "Track 1", difficulty: 0) @@ -201,9 +211,10 @@ Track.create(title: "Track 4", difficulty: 3) puts("# -----------------------Solutions---------------------------") - Solution.create(code:"println(My first solution)", length:5, status:1) + Solution.create(code: "println(My first submitted solution);", length: 5, status: 1) Solution.create(code:"println(My second solution)", length:5, status:0) Solution.create(code:"println(My third solution)", length:5, status:3) + Solution.create(code: "println(My first solution) \n int x =10;\n x++;\nDouble y\n y = x/3;", length: 5, status: 0) puts("# ----------------------- TrackProgression ----------------------- ") TrackProgression.create(level: 1, topic_id: 1) @@ -247,9 +258,11 @@ ContestProgress.create!(status:false) puts("# ----------------------- Assignments ----------------------- ") - Assignment.create(title:"DSD Assignment_1", publish: true) - Assignment.create(title:"DMENT Assignment_2", publish: true) - Assignment.create(title:"DSD Assignment_3", publish: true) + Assignment.create(title:"DSD Assignment_1", publish: true, due_date: Date.new(2009,6,13), + description:"This is your first DSD assignment. It contains exersices on basics of logic design.") + Assignment.create(title:"DMENT Assignment_2", publish: true, due_date: Date.new(2015,1,1)) + Assignment.create(title:"DSD Assignment_3", publish: true, due_date: DateTime.now.to_date) + Assignment.create(title:"CA Assignment", publish: false, description:"Allows practice on instruction set formats.", due_date: DateTime.now.to_date) puts("# ----------------------- Grades ----------------------- ") Grade.create(grade: 100) @@ -314,6 +327,7 @@ Lecturer.first.assignments << Assignment.first Lecturer.first.assignments << Assignment.find_by_id(2) Lecturer.first.assignments << Assignment.find_by_id(3) + Lecturer.first.assignments << Assignment.find_by_id(4) ## Grades Lecturer.first.grades << Grade.first Lecturer.find_by_id(2).grades << Grade.find_by_id(2) @@ -323,6 +337,7 @@ Student.first.course_students << CourseStudent.first ## Solutions Student.first.solutions << Solution.first + Student.first.solutions << Solution.find_by_id(4) Student.first.solutions << Solution.find_by_id(2) Student.first.solutions << Solution.find_by_id(3) ## Attempts @@ -405,7 +420,8 @@ Problem.find_by_id(3).model_answers << ModelAnswer.find_by_id(5) Problem.find_by_id(3).model_answers << ModelAnswer.find_by_id(6) ## Solutions - Problem.first.solutions << Solution.first + AssignmentProblem.first.solutions << Solution.first + AssignmentProblem.first.solutions << Solution.find_by_id(4) Problem.find_by_id(2).solutions << Solution.find_by_id(2) Problem.find_by_id(3).solutions << Solution.find_by_id(3) ## Attempts @@ -430,6 +446,7 @@ Cproblem.find_by_id(4).contests_progresses << ContestProgress.find_by_id(4) ## Hints Problem.first.model_answers.first.hints << Hint.first + Problem.first.model_answers.first.hints << Hint.all.second puts("# ----------------------- Tracks ----------------------- ") ## Problems @@ -464,6 +481,7 @@ ## Assignments Course.first.assignments << Assignment.first Course.first.assignments << Assignment.find_by_id(2) + Course.first.assignments << Assignment.find_by_id(4) Course.find_by_id(2).assignments << Assignment.find_by_id(3) diff --git a/tutor/spec/controllers/topics_controller_spec.rb b/tutor/spec/controllers/topics_controller_spec.rb new file mode 100644 index 00000000..08c30985 --- /dev/null +++ b/tutor/spec/controllers/topics_controller_spec.rb @@ -0,0 +1,25 @@ +require 'spec_helper' + +include Devise::TestHelpers + +describe TopicsController do + + context "Create Topic and deletes it" do + it "destroys the requested topic" do + lecturer = Lecturer.new(email: '1@lecturer.com', password: '123456789', + password_confirmation: '123456789', name: 'LecturerI', + confirmed_at: Time.now, dob: DateTime.now.to_date, gender: true, + degree: "PhD", university: "GUC", department: "MET") + lecturer.save! + course = Course.create(name:"Data Structures and Alogrithms", + description:"This is a very easy course", code:"CSEN1", year:2014, semester:1) + topic = Topic.create(title: "Topic1", description: "This is Topic1 description", + lecturer_id: lecturer.id, course_id: course.id) + TopicsController.skip_before_filter :authenticate! + expect { + delete :destroy, :id => topic.id + }.to change(Topic, :count).by(-1) + end + + end +end \ No newline at end of file diff --git a/tutor/spec/models/admin_user_spec.rb b/tutor/spec/models/admin_user_spec.rb new file mode 100644 index 00000000..3d153022 --- /dev/null +++ b/tutor/spec/models/admin_user_spec.rb @@ -0,0 +1,5 @@ +require 'spec_helper' + +describe AdminUser do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/tutor/spec/models/assignment_problem_spec.rb b/tutor/spec/models/assignment_problem_spec.rb new file mode 100644 index 00000000..a4f2ffed --- /dev/null +++ b/tutor/spec/models/assignment_problem_spec.rb @@ -0,0 +1,36 @@ +require 'spec_helper' + +describe AssignmentProblem do + it 'is valid with a title and description' do + assignment = AssignmentProblem.new( + title: 'rspec assignemt problem', + description: 'This is an assignment problem test') + expect(assignment).to be_valid + end + + it 'is invalid without title' do + assignment = AssignmentProblem.new( + description: 'This is a test assignemnt' + ) + expect(assignment).not_to be_valid + end + + it ' is added successfully to an asssignmet ' do + aproblem1 = AssignmentProblem.new( + title: 'P1', + description: 'P2', + ) + aproblem2 = AssignmentProblem.new( + title: 'P1', + description: 'P2', + ) + + assignment = Assignment.new( + title: 'rspec assignemt', + description: 'This is a test assignemnt', + due_date: Date.new(2015,1,1) , + problems: [aproblem1,aproblem2]) + + expect(assignment.problems).to include(aproblem1) + end +end \ No newline at end of file diff --git a/tutor/spec/models/assignment_spec.rb b/tutor/spec/models/assignment_spec.rb new file mode 100644 index 00000000..c8cb59c4 --- /dev/null +++ b/tutor/spec/models/assignment_spec.rb @@ -0,0 +1,46 @@ +require 'spec_helper' + +describe Assignment do + it 'is valid with a title and description' do + assignment = Assignment.new( + title: 'rspec assignemt', + description: 'This is a test assignemnt', + due_date: Date.new(2015,1,1)) + expect(assignment).to be_valid + end + + it 'is invalid without title' do + assignment = Assignment.new( + description: 'This is a test assignemnt', + due_date: Date.new(2015,1,1) + ) + expect(assignment).not_to be_valid + end + + it 'is valid if it includes problems' do + + aproblem1 = AssignmentProblem.new( + title: 'P1', + description: 'P2') + aproblem2 = AssignmentProblem.new( + title: 'P1', + description: 'p2') + + assignment = Assignment.new( + title: 'rspec assignemt', + description: 'This is a test assignemnt', + due_date: Date.new(2015,1,1), problems:[aproblem1 , aproblem2]) + + expect(assignment.problems[0]).to eq(aproblem1) + expect(assignment.problems[1]).to eq(aproblem2) + end + + it 'Changes the number of assignment' do + assignment = Assignment.new( + title: 'rspec assignemt', + description: 'This is a test assignemnt', + due_date: Date.new(2015,1,1)) + + expect {assignment.save}.to change {Assignment.count}.by(1) + end +end \ No newline at end of file diff --git a/tutor/spec/models/note_spec.rb b/tutor/spec/models/note_spec.rb new file mode 100644 index 00000000..c13ca5d2 --- /dev/null +++ b/tutor/spec/models/note_spec.rb @@ -0,0 +1,62 @@ +require 'spec_helper' + +describe Note do + it 'is valid with a content, solution_id and line' do + solution = Solution.new( + code: "println(My first solution)\n + int x =10;\n x++;\n Double y\n y = x/3;", + length: 5, + status: 1) + solution.save + note = Note.new( + content: 'This is a comment on a line of code', + line: 1, + solution_id: solution.id) + expect(note).to be_valid + end + + it 'is invalid without content' do + solution = Solution.new( + code: "println(My first solution)\n + int x =10;\n x++;\n Double y\n y = x/3;", + length: 5, + status: 1) + note = Note.new( + line: 1, + solution_id: solution.id) + expect(note).not_to be_valid + end + + it 'is invalid without line' do + solution = Solution.new( + code: "println(My first solution)\n + int x =10;\n x++;\n Double y\n y = x/3;", + length: 5, + status: 1) + note = Note.new( + content: "This is a comment on a line of code", + solution_id: solution.id) + expect(note).not_to be_valid + end + + it 'is invalid without solution_id' do + note = Note.new( + content: "This is a comment on a line of code", + line: 1) + expect(note).not_to be_valid + end + + it 'Changes the number of notes' do + solution = Solution.new( + code: "println(My first solution)\n + int x =10;\n x++;\n Double y\n y = x/3;", + length: 5, + status: 1) + solution.save + note = Note.new( + content: "This is a comment on a line of code", + line: 1, + solution_id: solution.id) + expect {note.save}.to change {Note.count}.by(1) + end +end \ No newline at end of file diff --git a/tutor/spec/models/notification_spec.rb b/tutor/spec/models/notification_spec.rb new file mode 100644 index 00000000..74df8c73 --- /dev/null +++ b/tutor/spec/models/notification_spec.rb @@ -0,0 +1,5 @@ +require 'spec_helper' + +describe Notification do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/tutor/spec/models/solutions_layer_spec.rb b/tutor/spec/models/solutions_layer_spec.rb index efff8001..6d40a5ed 100644 --- a/tutor/spec/models/solutions_layer_spec.rb +++ b/tutor/spec/models/solutions_layer_spec.rb @@ -7,18 +7,18 @@ expect(SolutionsLayer.get_compiler "java").to eq JavaCompiler end - it "no such compiler named blabla" do - expect(SolutionsLayer.get_compiler "blabla").to eq false + it "no such compiler named foo" do + expect(SolutionsLayer.get_compiler "foo").to eq false end end context "get_executer tests" do it "get java executer" do - expect(SolutionsLayer.get_executer "java").to eq JavaExecuter + expect(SolutionsLayer.get_executer foo"java").to eq JavaExecuter end - it "raise error when trying to get a non valid blabla executer" do - expect{SolutionsLayer.get_executer "blabla"}.to raise_error(NameError) + it "no such executer named foo" do + expect(SolutionsLayer.get_executer "foo").to eq false end end @@ -27,8 +27,8 @@ expect(SolutionsLayer.get_debugger "java").to eq JavaDebugger end - it "no such debugger named blabla" do - expect(SolutionsLayer.get_debugger "blabla").to eq false + it "no such debugger named foo" do + expect(SolutionsLayer.get_debugger "foo").to eq false end end @@ -43,41 +43,44 @@ description: "Given two numbers a and b, output a/b", incomplete: false) test_cases = TestCase.create(output: "5\n", input:"10 2") problem.test_cases << test_cases - @code = "public static void main(String [] args){}}" + @code = "public class Coolsoft{public static void main(String [] args){}}" @student_id = 1 @problem_id = problem.id @lang = "java" @cases1 = "1 12 123" @cases2 = "1 12" + @problem_type = "Problem" + @class_name = "Coolsoft" @solution = Solution.create(code: @code, student_id: @student_id, - problem_id: @problem_id) + problem_id: @problem_id, problem_type: @problem_type, class_name: @class_name) end context "compiler" do it "a solution is created" do expect(Solution).to receive(:create).and_return(@solution) - SolutionsLayer.compile @lang, @code, @student_id, @problem_id + SolutionsLayer.compile @lang, @code, @student_id, @problem_id, @problem_type, @class_name end it "java JavaCompiler.compiler_feedback was called" do expect(JavaCompiler).to receive(:compiler_feedback){ {success: false, errors: ""} } - SolutionsLayer.compile @lang, @code, @student_id, @problem_id + SolutionsLayer.compile @lang, @code, @student_id, @problem_id, @problem_type, @class_name end it "get_compiler was called and JavaCompiler was returned" do expect(SolutionsLayer).to receive(:get_compiler).with("java"){JavaCompiler} - SolutionsLayer.compile @lang, @code, @student_id, @problem_id + SolutionsLayer.compile @lang, @code, @student_id, @problem_id, @problem_type, @class_name end it "get_solution was called and new Solution was returned" do expect(SolutionsLayer).to receive(:get_solution).and_return(@solution) - SolutionsLayer.compile @lang, @code, @student_id, @problem_id + SolutionsLayer.compile @lang, @code, @student_id, @problem_id, @problem_type, @class_name end it "Compiled successfuly" do - feed_back = SolutionsLayer.compile @lang, @code, @student_id, @problem_id + feed_back = SolutionsLayer.compile(@lang, @code, @student_id, + @problem_id, @problem_type, @class_name) expected_hash = {success: true, errors: nil} expect(feed_back).to eq expected_hash end @@ -86,17 +89,20 @@ context "executer" do it "get_compiler was called and JavaCompiler was returned" do expect(SolutionsLayer).to receive(:get_compiler).with("java"){JavaCompiler} - SolutionsLayer.execute @lang, @code, @student_id, @problem_id, @cases1 + SolutionsLayer.execute(@lang, @code, @student_id, + @problem_id, @problem_type, @class_name, @cases1) end it "Compiled successfuly but wrong input" do - feed_back = SolutionsLayer.execute @lang, @code, @student_id, @problem_id, @cases1 + feed_back = SolutionsLayer.execute(@lang, @code, @student_id, + @problem_id, @problem_type, @class_name, @cases1) expected_hash = {executer_feedback: false, executer_output: "Enter only 2 numbers"} expect(feed_back).to eq expected_hash end it "Compiled successfuly and run successfuly with no output" do - feed_back = SolutionsLayer.execute @lang, @code, @student_id, @problem_id, @cases2 + feed_back = SolutionsLayer.execute(@lang, @code, @student_id, + @problem_id, @problem_type, @class_name, @cases2) expected_hash = {executer_feedback: true, executer_output: {success: true, message: ""} } @@ -104,21 +110,24 @@ end it "Compilation error" do - code = "public static void main(String [] args){System.out.println(5)}}" - feed_back = SolutionsLayer.execute @lang, code, @student_id, @problem_id, @cases2 - expected_hash = {compiler_error: true, - compiler_output: {success: false, - errors: "CoolSoft.java:2: error: ';' expected\npublic " + - "static void main(String [] args){System.out.println(5)}}" + - "\n " + - " ^\n1 error\n"} + code = "public class Coolsoft{public static void main" + + "(String [] args){System.out.println(5)}}" + feed_back = SolutionsLayer.execute(@lang, code, @student_id, + @problem_id, @problem_type, @class_name, @cases2) + expected_hash = {compiler_error: true, compiler_output: {success: false, + errors: "Coolsoft.java:1: error: ';' expected\npublic " + + "class Coolsoft{public static void main(String [] args)" + + "{System.out.println(5)}}\n " + + " ^\n1 error\n"} } expect(feed_back).to eq expected_hash end it "Compiled successfuly and run successfuly with output" do - code = "public static void main(String [] args){System.out.println(5);}}" - feed_back = SolutionsLayer.execute @lang, code, @student_id, @problem_id, @cases2 + code = "public class Coolsoft{public static void main" + + "(String [] args){System.out.println(5);}}" + feed_back = SolutionsLayer.execute(@lang, code, @student_id, + @problem_id, @problem_type, @class_name, @cases2) expected_hash = {executer_feedback: true, executer_output: {success: true, message: "5\n"} } @@ -129,33 +138,41 @@ context "debugger" do it "get_solution called" do expect(SolutionsLayer).to receive(:get_solution).and_return(@solution) - SolutionsLayer.debug @lang, @code, @student_id, @problem_id, @cases1 + SolutionsLayer.debug(@lang, @code, @student_id, + @problem_id, @problem_type, @class_name, @cases1) end it "get_compiler called" do expect(SolutionsLayer).to receive(:get_compiler).and_return(JavaCompiler) - SolutionsLayer.debug @lang, @code, @student_id, @problem_id, @cases1 + SolutionsLayer.debug(@lang, @code, @student_id, + @problem_id, @problem_type, @class_name, @cases1) end it "get_debugger called" do expect(SolutionsLayer).to receive(:get_debugger).and_return(JavaDebugger) - SolutionsLayer.debug @lang, @code, @student_id, @problem_id, @cases1 + SolutionsLayer.debug(@lang, @code, @student_id, + @problem_id, @problem_type, @class_name, @cases1) end it "Compilation error" do - code = "public static void main(String [] args){System.out.println(5)}}" - feed_back = SolutionsLayer.debug @lang, code, @student_id, @problem_id, @cases1 - expected_hash = {data: {success: false, errors: "CoolSoft.java:2: error: ';'" + - " expected\npublic static void main(String [] args){" + - "System.out.println(5)}}\n " + - " ^\n1 error\n"}, success: false} + code = "public class Coolsoft{public static void main" + + "(String [] args){System.out.println(5)}}" + feed_back = SolutionsLayer.debug(@lang, code, @student_id, + @problem_id, @problem_type, @class_name, @cases1) + expected_hash = {success: false, data: {success: false, + errors: "Coolsoft.java:1: error: ';'" + + " expected\npublic class Coolsoft{public static void main(String [] args){" + + "System.out.println(5)}}\n " + + " ^\n1 error\n"}} expect(feed_back).to eq expected_hash end it "Debugged successfuly", focus: true do - feed_back = SolutionsLayer.debug @lang, @code, @student_id, @problem_id, @cases1 - expected_hash = {data: [{status: true, line: 2, stream: "", - locals: [" args = {\n\"1\", \"12\", \"123\"\n}\n"]}], success: true} + feed_back = SolutionsLayer.debug(@lang, @code, @student_id, + @problem_id, @problem_type, @class_name, @cases1) + expected_hash = {data: [{status: true, line: 1, stream: "", + locals: [" args = {\n\"1\", \"12\", \"123\"\n}\n"]}], success: true, + status: "The debugging session was successful."} expect(feed_back).to eq expected_hash end end diff --git a/tutor/spec/models/time_limit_spec.rb b/tutor/spec/models/time_limit_spec.rb new file mode 100644 index 00000000..c03d880e --- /dev/null +++ b/tutor/spec/models/time_limit_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper' + +describe TimeLimit do + context "TimeLimit Execeded" do + it "sleeping" do + expect(TimeLimit.start 5, "sleep 20").not_to raise_error + end + + it "infinite loop" do + expect(TimeLimit.start(5){ + while true + end + }).not_to raise_error + end + end + + context "TimeLimit wasnot Execeded" do + it "echo foo" do + expect(TimeLimit.start 1, "echo foo").to eq [true, "foo\n", ""] + end + end +end diff --git a/tutor/test/controllers/assignment_problems_controller_test.rb b/tutor/test/controllers/assignment_problems_controller_test.rb new file mode 100644 index 00000000..2069f2a4 --- /dev/null +++ b/tutor/test/controllers/assignment_problems_controller_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class AssignmentProblemsControllerTest < ActionController::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/tutor/test/controllers/assignments_controller_test.rb b/tutor/test/controllers/assignments_controller_test.rb new file mode 100644 index 00000000..8018cb5e --- /dev/null +++ b/tutor/test/controllers/assignments_controller_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class AssignmentsControllerTest < ActionController::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/tutor/test/fixtures/notes.yml b/tutor/test/fixtures/notes.yml new file mode 100644 index 00000000..937a0c00 --- /dev/null +++ b/tutor/test/fixtures/notes.yml @@ -0,0 +1,11 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +# This model initially had no columns defined. If you add columns to the +# model remove the '{}' from the fixture names and add the columns immediately +# below each fixture, per the syntax in the comments below +# +one: {} +# column: value +# +two: {} +# column: value diff --git a/tutor/test/helpers/assignment_problems_helper_test.rb b/tutor/test/helpers/assignment_problems_helper_test.rb new file mode 100644 index 00000000..8953bc37 --- /dev/null +++ b/tutor/test/helpers/assignment_problems_helper_test.rb @@ -0,0 +1,4 @@ +require 'test_helper' + +class AssignmentProblemsHelperTest < ActionView::TestCase +end diff --git a/tutor/test/helpers/assignments_helper_test.rb b/tutor/test/helpers/assignments_helper_test.rb new file mode 100644 index 00000000..f077f005 --- /dev/null +++ b/tutor/test/helpers/assignments_helper_test.rb @@ -0,0 +1,4 @@ +require 'test_helper' + +class AssignmentsHelperTest < ActionView::TestCase +end diff --git a/tutor/test/models/note_test.rb b/tutor/test/models/note_test.rb new file mode 100644 index 00000000..7bbab537 --- /dev/null +++ b/tutor/test/models/note_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class NoteTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/tutor/vendor/assets/javascripts/active_admin.js.coffee b/tutor/vendor/assets/javascripts/active_admin.js.coffee new file mode 100644 index 00000000..3752dcef --- /dev/null +++ b/tutor/vendor/assets/javascripts/active_admin.js.coffee @@ -0,0 +1 @@ +#= require active_admin/base diff --git a/tutor/vendor/assets/javascripts/bootstrap.js b/tutor/vendor/assets/javascripts/bootstrap.js index c5f6cf0b..a2cde25d 100644 --- a/tutor/vendor/assets/javascripts/bootstrap.js +++ b/tutor/vendor/assets/javascripts/bootstrap.js @@ -1952,3 +1952,967 @@ if (typeof jQuery === 'undefined') { throw new Error('Bootstrap\'s JavaScript re }) }(jQuery); +!function($) { + + 'use strict'; + + $.expr[':'].icontains = function(obj, index, meta) { + return $(obj).text().toUpperCase().indexOf(meta[3].toUpperCase()) >= 0; + }; + + var Selectpicker = function(element, options, e) { + if (e) { + e.stopPropagation(); + e.preventDefault(); + } + this.$element = $(element); + this.$newElement = null; + this.$button = null; + this.$menu = null; + this.$lis = null; + + //Merge defaults, options and data-attributes to make our options + this.options = $.extend({}, $.fn.selectpicker.defaults, this.$element.data(), typeof options == 'object' && options); + + //If we have no title yet, check the attribute 'title' (this is missed by jq as its not a data-attribute + if (this.options.title === null) { + this.options.title = this.$element.attr('title'); + } + + //Expose public methods + this.val = Selectpicker.prototype.val; + this.render = Selectpicker.prototype.render; + this.refresh = Selectpicker.prototype.refresh; + this.setStyle = Selectpicker.prototype.setStyle; + this.selectAll = Selectpicker.prototype.selectAll; + this.deselectAll = Selectpicker.prototype.deselectAll; + this.init(); + }; + + Selectpicker.prototype = { + + constructor: Selectpicker, + + init: function() { + var that = this, + id = this.$element.attr('id'); + + this.$element.hide(); + this.multiple = this.$element.prop('multiple'); + this.autofocus = this.$element.prop('autofocus'); + this.$newElement = this.createView(); + this.$element.after(this.$newElement); + this.$menu = this.$newElement.find('> .dropdown-menu'); + this.$button = this.$newElement.find('> button'); + this.$searchbox = this.$newElement.find('input'); + + if (id !== undefined) { + this.$button.attr('data-id', id); + $('label[for="' + id + '"]').click(function(e) { + e.preventDefault(); + that.$button.focus(); + }); + } + + this.checkDisabled(); + this.clickListener(); + if (this.options.liveSearch) this.liveSearchListener(); + this.render(); + this.liHeight(); + this.setStyle(); + this.setWidth(); + if (this.options.container) this.selectPosition(); + this.$menu.data('this', this); + this.$newElement.data('this', this); + }, + + createDropdown: function() { + //If we are multiple, then add the show-tick class by default + var multiple = this.multiple ? ' show-tick' : ''; + var inputGroup = this.$element.parent().hasClass('input-group') ? ' input-group-btn' : ''; + var autofocus = this.autofocus ? ' autofocus' : ''; + var header = this.options.header ? '
    ' + this.options.header + '
    ' : ''; + var searchbox = this.options.liveSearch ? '' : ''; + var actionsbox = this.options.actionsBox ? '
    ' + + '
    ' + + '' + + '' + + '
    ' + + '
    ' : ''; + var drop = + '
    ' + + '' + + '' + + '
    '; + + return $(drop); + }, + + createView: function() { + var $drop = this.createDropdown(); + var $li = this.createLi(); + $drop.find('ul').append($li); + return $drop; + }, + + reloadLi: function() { + //Remove all children. + this.destroyLi(); + //Re build + var $li = this.createLi(); + this.$menu.find('ul').append( $li ); + }, + + destroyLi: function() { + this.$menu.find('li').remove(); + }, + + createLi: function() { + var that = this, + _liA = [], + _liHtml = ''; + + this.$element.find('option').each(function() { + var $this = $(this); + + //Get the class and text for the option + var optionClass = $this.attr('class') || ''; + var inline = $this.attr('style') || ''; + var text = $this.data('content') ? $this.data('content') : $this.html(); + var subtext = $this.data('subtext') !== undefined ? '' + $this.data('subtext') + '' : ''; + var icon = $this.data('icon') !== undefined ? ' ' : ''; + if (icon !== '' && ($this.is(':disabled') || $this.parent().is(':disabled'))) { + icon = ''+icon+''; + } + + if (!$this.data('content')) { + //Prepend any icon and append any subtext to the main text. + text = icon + '' + text + subtext + ''; + } + + if (that.options.hideDisabled && ($this.is(':disabled') || $this.parent().is(':disabled'))) { + _liA.push(''); + } else if ($this.parent().is('optgroup') && $this.data('divider') !== true) { + if ($this.index() === 0) { + //Get the opt group label + var label = $this.parent().attr('label'); + var labelSubtext = $this.parent().data('subtext') !== undefined ? ''+$this.parent().data('subtext')+'' : ''; + var labelIcon = $this.parent().data('icon') ? ' ' : ''; + label = labelIcon + '' + label + labelSubtext + ''; + + if ($this[0].index !== 0) { + _liA.push( + '
    '+ + '
    '+label+'
    '+ + that.createA(text, 'opt ' + optionClass, inline ) + ); + } else { + _liA.push( + '
    '+label+'
    '+ + that.createA(text, 'opt ' + optionClass, inline )); + } + } else { + _liA.push(that.createA(text, 'opt ' + optionClass, inline )); + } + } else if ($this.data('divider') === true) { + _liA.push('
    '); + } else if ($(this).data('hidden') === true) { + _liA.push(''); + } else { + _liA.push(that.createA(text, optionClass, inline )); + } + }); + + $.each(_liA, function(i, item) { + var hide = item === '' ? 'class="hide is-hidden"' : ''; + _liHtml += '
  • ' + item + '
  • '; + }); + + //If we are not multiple, and we dont have a selected item, and we dont have a title, select the first element so something is set in the button + if (!this.multiple && this.$element.find('option:selected').length===0 && !this.options.title) { + this.$element.find('option').eq(0).prop('selected', true).attr('selected', 'selected'); + } + + return $(_liHtml); + }, + + createA: function(text, classes, inline) { + return '' + + text + + '' + + ''; + }, + + render: function(updateLi) { + var that = this; + + //Update the LI to match the SELECT + if (updateLi !== false) { + this.$element.find('option').each(function(index) { + that.setDisabled(index, $(this).is(':disabled') || $(this).parent().is(':disabled') ); + that.setSelected(index, $(this).is(':selected') ); + }); + } + + this.tabIndex(); + + var selectedItems = this.$element.find('option:selected').map(function() { + var $this = $(this); + var icon = $this.data('icon') && that.options.showIcon ? ' ' : ''; + var subtext; + if (that.options.showSubtext && $this.attr('data-subtext') && !that.multiple) { + subtext = ' '+$this.data('subtext') +''; + } else { + subtext = ''; + } + if ($this.data('content') && that.options.showContent) { + return $this.data('content'); + } else if ($this.attr('title') !== undefined) { + return $this.attr('title'); + } else { + return icon + $this.html() + subtext; + } + }).toArray(); + + //Fixes issue in IE10 occurring when no default option is selected and at least one option is disabled + //Convert all the values into a comma delimited string + var title = !this.multiple ? selectedItems[0] : selectedItems.join(this.options.multipleSeparator); + + //If this is multi select, and the selectText type is count, the show 1 of 2 selected etc.. + if (this.multiple && this.options.selectedTextFormat.indexOf('count') > -1) { + var max = this.options.selectedTextFormat.split('>'); + var notDisabled = this.options.hideDisabled ? ':not([disabled])' : ''; + if ( (max.length>1 && selectedItems.length > max[1]) || (max.length==1 && selectedItems.length>=2)) { + title = this.options.countSelectedText.replace('{0}', selectedItems.length).replace('{1}', this.$element.find('option:not([data-divider="true"]):not([data-hidden="true"])'+notDisabled).length); + } + } + + this.options.title = this.$element.attr('title'); + + //If we dont have a title, then use the default, or if nothing is set at all, use the not selected text + if (!title) { + title = this.options.title !== undefined ? this.options.title : this.options.noneSelectedText; + } + + this.$button.attr('title', $.trim(title)); + this.$newElement.find('.filter-option').html(title); + }, + + setStyle: function(style, status) { + if (this.$element.attr('class')) { + this.$newElement.addClass(this.$element.attr('class').replace(/selectpicker|mobile-device/gi, '')); + } + + var buttonClass = style ? style : this.options.style; + + if (status == 'add') { + this.$button.addClass(buttonClass); + } else if (status == 'remove') { + this.$button.removeClass(buttonClass); + } else { + this.$button.removeClass(this.options.style); + this.$button.addClass(buttonClass); + } + }, + + liHeight: function() { + if (this.options.size === false) return; + + var $selectClone = this.$menu.parent().clone().find('> .dropdown-toggle').prop('autofocus', false).end().appendTo('body'), + $menuClone = $selectClone.addClass('open').find('> .dropdown-menu'), + liHeight = $menuClone.find('li > a').outerHeight(), + headerHeight = this.options.header ? $menuClone.find('.popover-title').outerHeight() : 0, + searchHeight = this.options.liveSearch ? $menuClone.find('.bootstrap-select-searchbox').outerHeight() : 0, + actionsHeight = this.options.actionsBox ? $menuClone.find('.bs-actionsbox').outerHeight() : 0; + + $selectClone.remove(); + + this.$newElement + .data('liHeight', liHeight) + .data('headerHeight', headerHeight) + .data('searchHeight', searchHeight) + .data('actionsHeight', actionsHeight); + }, + + setSize: function() { + var that = this, + menu = this.$menu, + menuInner = menu.find('.inner'), + selectHeight = this.$newElement.outerHeight(), + liHeight = this.$newElement.data('liHeight'), + headerHeight = this.$newElement.data('headerHeight'), + searchHeight = this.$newElement.data('searchHeight'), + actionsHeight = this.$newElement.data('actionsHeight'), + divHeight = menu.find('li .divider').outerHeight(true), + menuPadding = parseInt(menu.css('padding-top')) + + parseInt(menu.css('padding-bottom')) + + parseInt(menu.css('border-top-width')) + + parseInt(menu.css('border-bottom-width')), + notDisabled = this.options.hideDisabled ? ':not(.disabled)' : '', + $window = $(window), + menuExtras = menuPadding + parseInt(menu.css('margin-top')) + parseInt(menu.css('margin-bottom')) + 2, + menuHeight, + selectOffsetTop, + selectOffsetBot, + posVert = function() { + selectOffsetTop = that.$newElement.offset().top - $window.scrollTop(); + selectOffsetBot = $window.height() - selectOffsetTop - selectHeight; + }; + posVert(); + if (this.options.header) menu.css('padding-top', 0); + + if (this.options.size == 'auto') { + var getSize = function() { + var minHeight, + lisVis = that.$lis.not('.hide'); + + posVert(); + menuHeight = selectOffsetBot - menuExtras; + + if (that.options.dropupAuto) { + that.$newElement.toggleClass('dropup', (selectOffsetTop > selectOffsetBot) && ((menuHeight - menuExtras) < menu.height())); + } + if (that.$newElement.hasClass('dropup')) { + menuHeight = selectOffsetTop - menuExtras; + } + + if ((lisVis.length + lisVis.find('dt').length) > 3) { + minHeight = liHeight*3 + menuExtras - 2; + } else { + minHeight = 0; + } + + menu.css({'max-height' : menuHeight + 'px', 'overflow' : 'hidden', 'min-height' : minHeight + headerHeight + searchHeight + actionsHeight + 'px'}); + menuInner.css({'max-height' : menuHeight - headerHeight - searchHeight - actionsHeight - menuPadding + 'px', 'overflow-y' : 'auto', 'min-height' : Math.max(minHeight - menuPadding, 0) + 'px'}); + }; + getSize(); + this.$searchbox.off('input.getSize propertychange.getSize').on('input.getSize propertychange.getSize', getSize); + $(window).off('resize.getSize').on('resize.getSize', getSize); + $(window).off('scroll.getSize').on('scroll.getSize', getSize); + } else if (this.options.size && this.options.size != 'auto' && menu.find('li'+notDisabled).length > this.options.size) { + var optIndex = menu.find('li'+notDisabled+' > *').filter(':not(.div-contain)').slice(0,this.options.size).last().parent().index(); + var divLength = menu.find('li').slice(0,optIndex + 1).find('.div-contain').length; + menuHeight = liHeight*this.options.size + divLength*divHeight + menuPadding; + if (that.options.dropupAuto) { + this.$newElement.toggleClass('dropup', (selectOffsetTop > selectOffsetBot) && (menuHeight < menu.height())); + } + menu.css({'max-height' : menuHeight + headerHeight + searchHeight + actionsHeight + 'px', 'overflow' : 'hidden'}); + menuInner.css({'max-height' : menuHeight - menuPadding + 'px', 'overflow-y' : 'auto'}); + } + }, + + setWidth: function() { + if (this.options.width == 'auto') { + this.$menu.css('min-width', '0'); + + // Get correct width if element hidden + var selectClone = this.$newElement.clone().appendTo('body'); + var ulWidth = selectClone.find('> .dropdown-menu').css('width'); + var btnWidth = selectClone.css('width', 'auto').find('> button').css('width'); + selectClone.remove(); + + // Set width to whatever's larger, button title or longest option + this.$newElement.css('width', Math.max(parseInt(ulWidth), parseInt(btnWidth)) + 'px'); + } else if (this.options.width == 'fit') { + // Remove inline min-width so width can be changed from 'auto' + this.$menu.css('min-width', ''); + this.$newElement.css('width', '').addClass('fit-width'); + } else if (this.options.width) { + // Remove inline min-width so width can be changed from 'auto' + this.$menu.css('min-width', ''); + this.$newElement.css('width', this.options.width); + } else { + // Remove inline min-width/width so width can be changed + this.$menu.css('min-width', ''); + this.$newElement.css('width', ''); + } + // Remove fit-width class if width is changed programmatically + if (this.$newElement.hasClass('fit-width') && this.options.width !== 'fit') { + this.$newElement.removeClass('fit-width'); + } + }, + + selectPosition: function() { + var that = this, + drop = '
    ', + $drop = $(drop), + pos, + actualHeight, + getPlacement = function($element) { + $drop.addClass($element.attr('class').replace(/form-control/gi, '')).toggleClass('dropup', $element.hasClass('dropup')); + pos = $element.offset(); + actualHeight = $element.hasClass('dropup') ? 0 : $element[0].offsetHeight; + $drop.css({'top' : pos.top + actualHeight, 'left' : pos.left, 'width' : $element[0].offsetWidth, 'position' : 'absolute'}); + }; + this.$newElement.on('click', function() { + if (that.isDisabled()) { + return; + } + getPlacement($(this)); + $drop.appendTo(that.options.container); + $drop.toggleClass('open', !$(this).hasClass('open')); + $drop.append(that.$menu); + }); + $(window).resize(function() { + getPlacement(that.$newElement); + }); + $(window).on('scroll', function() { + getPlacement(that.$newElement); + }); + $('html').on('click', function(e) { + if ($(e.target).closest(that.$newElement).length < 1) { + $drop.removeClass('open'); + } + }); + }, + + mobile: function() { + this.$element.addClass('mobile-device').appendTo(this.$newElement); + if (this.options.container) this.$menu.hide(); + }, + + refresh: function() { + this.$lis = null; + this.reloadLi(); + this.render(); + this.setWidth(); + this.setStyle(); + this.checkDisabled(); + this.liHeight(); + }, + + update: function() { + this.reloadLi(); + this.setWidth(); + this.setStyle(); + this.checkDisabled(); + this.liHeight(); + }, + + setSelected: function(index, selected) { + if (this.$lis == null) this.$lis = this.$menu.find('li'); + $(this.$lis[index]).toggleClass('selected', selected); + }, + + setDisabled: function(index, disabled) { + if (this.$lis == null) this.$lis = this.$menu.find('li'); + if (disabled) { + $(this.$lis[index]).addClass('disabled').find('a').attr('href', '#').attr('tabindex', -1); + } else { + $(this.$lis[index]).removeClass('disabled').find('a').removeAttr('href').attr('tabindex', 0); + } + }, + + isDisabled: function() { + return this.$element.is(':disabled'); + }, + + checkDisabled: function() { + var that = this; + + if (this.isDisabled()) { + this.$button.addClass('disabled').attr('tabindex', -1); + } else { + if (this.$button.hasClass('disabled')) { + this.$button.removeClass('disabled'); + } + + if (this.$button.attr('tabindex') == -1) { + if (!this.$element.data('tabindex')) this.$button.removeAttr('tabindex'); + } + } + + this.$button.click(function() { + return !that.isDisabled(); + }); + }, + + tabIndex: function() { + if (this.$element.is('[tabindex]')) { + this.$element.data('tabindex', this.$element.attr('tabindex')); + this.$button.attr('tabindex', this.$element.data('tabindex')); + } + }, + + clickListener: function() { + var that = this; + + $('body').on('touchstart.dropdown', '.dropdown-menu', function(e) { + e.stopPropagation(); + }); + + this.$newElement.on('click', function() { + that.setSize(); + if (!that.options.liveSearch && !that.multiple) { + setTimeout(function() { + that.$menu.find('.selected a').focus(); + }, 10); + } + }); + + this.$menu.on('click', 'li a', function(e) { + var clickedIndex = $(this).parent().index(), + prevValue = that.$element.val(), + prevIndex = that.$element.prop('selectedIndex'); + + //Dont close on multi choice menu + if (that.multiple) { + e.stopPropagation(); + } + + e.preventDefault(); + + //Dont run if we have been disabled + if (!that.isDisabled() && !$(this).parent().hasClass('disabled')) { + var $options = that.$element.find('option'), + $option = $options.eq(clickedIndex), + state = $option.prop('selected'), + $optgroup = $option.parent('optgroup'), + maxOptions = that.options.maxOptions, + maxOptionsGrp = $optgroup.data('maxOptions') || false; + + //Deselect all others if not multi select box + if (!that.multiple) { + $options.prop('selected', false); + $option.prop('selected', true); + that.$menu.find('.selected').removeClass('selected'); + that.setSelected(clickedIndex, true); + } + //Else toggle the one we have chosen if we are multi select. + else { + $option.prop('selected', !state); + that.setSelected(clickedIndex, !state); + + if ((maxOptions !== false) || (maxOptionsGrp !== false)) { + var maxReached = maxOptions < $options.filter(':selected').length, + maxReachedGrp = maxOptionsGrp < $optgroup.find('option:selected').length, + maxOptionsArr = that.options.maxOptionsText, + maxTxt = maxOptionsArr[0].replace('{n}', maxOptions), + maxTxtGrp = maxOptionsArr[1].replace('{n}', maxOptionsGrp), + $notify = $('
    '); + + if ((maxOptions && maxReached) || (maxOptionsGrp && maxReachedGrp)) { + // If {var} is set in array, replace it + if (maxOptionsArr[2]) { + maxTxt = maxTxt.replace('{var}', maxOptionsArr[2][maxOptions > 1 ? 0 : 1]); + maxTxtGrp = maxTxtGrp.replace('{var}', maxOptionsArr[2][maxOptionsGrp > 1 ? 0 : 1]); + } + + $option.prop('selected', false); + + that.$menu.append($notify); + + if (maxOptions && maxReached) { + $notify.append($('
    ' + maxTxt + '
    ')); + that.$element.trigger('maxReached.bs.select'); + } + + if (maxOptionsGrp && maxReachedGrp) { + $notify.append($('
    ' + maxTxtGrp + '
    ')); + that.$element.trigger('maxReachedGrp.bs.select'); + } + + setTimeout(function() { + that.setSelected(clickedIndex, false); + }, 10); + + $notify.delay(750).fadeOut(300, function() { $(this).remove(); }); + } + } + } + + if (!that.multiple) { + that.$button.focus(); + } else if (that.options.liveSearch) { + that.$searchbox.focus(); + } + + // Trigger select 'change' + if ((prevValue != that.$element.val() && that.multiple) || (prevIndex != that.$element.prop('selectedIndex') && !that.multiple)) { + that.$element.change(); + } + } + }); + + this.$menu.on('click', 'li.disabled a, li dt, li .div-contain, .popover-title, .popover-title :not(.close)', function(e) { + if (e.target == this) { + e.preventDefault(); + e.stopPropagation(); + if (!that.options.liveSearch) { + that.$button.focus(); + } else { + that.$searchbox.focus(); + } + } + }); + + this.$menu.on('click', '.popover-title .close', function() { + that.$button.focus(); + }); + + this.$searchbox.on('click', function(e) { + e.stopPropagation(); + }); + + + this.$menu.on('click', '.actions-btn', function(e) { + if (that.options.liveSearch) { + that.$searchbox.focus(); + } else { + that.$button.focus(); + } + + e.preventDefault(); + e.stopPropagation(); + + if ($(this).is('.bs-select-all')) { + that.selectAll(); + } else { + that.deselectAll(); + } + that.$element.change(); + }); + + this.$element.change(function() { + that.render(false); + }); + }, + + liveSearchListener: function() { + var that = this, + no_results = $('
  • '); + + this.$newElement.on('click.dropdown.data-api', function() { + that.$menu.find('.active').removeClass('active'); + if (!!that.$searchbox.val()) { + that.$searchbox.val(''); + that.$lis.not('.is-hidden').removeClass('hide'); + if (!!no_results.parent().length) no_results.remove(); + } + if (!that.multiple) that.$menu.find('.selected').addClass('active'); + setTimeout(function() { + that.$searchbox.focus(); + }, 10); + }); + + this.$searchbox.on('input propertychange', function() { + if (that.$searchbox.val()) { + that.$lis.not('.is-hidden').removeClass('hide').find('a').not(':icontains(' + that.$searchbox.val() + ')').parent().addClass('hide'); + + if (!that.$menu.find('li').filter(':visible:not(.no-results)').length) { + if (!!no_results.parent().length) no_results.remove(); + no_results.html(that.options.noneResultsText + ' "'+ that.$searchbox.val() + '"').show(); + that.$menu.find('li').last().after(no_results); + } else if (!!no_results.parent().length) { + no_results.remove(); + } + + } else { + that.$lis.not('.is-hidden').removeClass('hide'); + if (!!no_results.parent().length) no_results.remove(); + } + + that.$menu.find('li.active').removeClass('active'); + that.$menu.find('li').filter(':visible:not(.divider)').eq(0).addClass('active').find('a').focus(); + $(this).focus(); + }); + + this.$menu.on('mouseenter', 'a', function(e) { + that.$menu.find('.active').removeClass('active'); + $(e.currentTarget).parent().not('.disabled').addClass('active'); + }); + + this.$menu.on('mouseleave', 'a', function() { + that.$menu.find('.active').removeClass('active'); + }); + }, + + val: function(value) { + + if (value !== undefined) { + this.$element.val( value ); + + this.$element.change(); + return this.$element; + } else { + return this.$element.val(); + } + }, + + selectAll: function() { + if (this.$lis == null) this.$lis = this.$menu.find('li'); + this.$element.find('option:enabled').prop('selected', true); + $(this.$lis).filter(':not(.disabled)').addClass('selected'); + this.render(false); + }, + + deselectAll: function() { + if (this.$lis == null) this.$lis = this.$menu.find('li'); + this.$element.find('option:enabled').prop('selected', false); + $(this.$lis).filter(':not(.disabled)').removeClass('selected'); + this.render(false); + }, + + keydown: function(e) { + var $this, + $items, + $parent, + index, + next, + first, + last, + prev, + nextPrev, + that, + prevIndex, + isActive, + keyCodeMap = { + 32:' ', 48:'0', 49:'1', 50:'2', 51:'3', 52:'4', 53:'5', 54:'6', 55:'7', 56:'8', 57:'9', 59:';', + 65:'a', 66:'b', 67:'c', 68:'d', 69:'e', 70:'f', 71:'g', 72:'h', 73:'i', 74:'j', 75:'k', 76:'l', + 77:'m', 78:'n', 79:'o', 80:'p', 81:'q', 82:'r', 83:'s', 84:'t', 85:'u', 86:'v', 87:'w', 88:'x', + 89:'y', 90:'z', 96:'0', 97:'1', 98:'2', 99:'3', 100:'4', 101:'5', 102:'6', 103:'7', 104:'8', 105:'9' + }; + + $this = $(this); + + $parent = $this.parent(); + + if ($this.is('input')) $parent = $this.parent().parent(); + + that = $parent.data('this'); + + if (that.options.liveSearch) $parent = $this.parent().parent(); + + if (that.options.container) $parent = that.$menu; + + $items = $('[role=menu] li:not(.divider) a', $parent); + + isActive = that.$menu.parent().hasClass('open'); + + if (!isActive && /([0-9]|[A-z])/.test(String.fromCharCode(e.keyCode))) { + if (!that.options.container) { + that.setSize(); + that.$menu.parent().addClass('open'); + isActive = that.$menu.parent().hasClass('open'); + } else { + that.$newElement.trigger('click'); + } + that.$searchbox.focus(); + } + + if (that.options.liveSearch) { + if (/(^9$|27)/.test(e.keyCode) && isActive && that.$menu.find('.active').length === 0) { + e.preventDefault(); + that.$menu.parent().removeClass('open'); + that.$button.focus(); + } + $items = $('[role=menu] li:not(.divider):visible', $parent); + if (!$this.val() && !/(38|40)/.test(e.keyCode)) { + if ($items.filter('.active').length === 0) { + $items = that.$newElement.find('li').filter(':icontains(' + keyCodeMap[e.keyCode] + ')'); + } + } + } + + if (!$items.length) return; + + if (/(38|40)/.test(e.keyCode)) { + + index = $items.index($items.filter(':focus')); + first = $items.parent(':not(.disabled):visible').first().index(); + last = $items.parent(':not(.disabled):visible').last().index(); + next = $items.eq(index).parent().nextAll(':not(.disabled):visible').eq(0).index(); + prev = $items.eq(index).parent().prevAll(':not(.disabled):visible').eq(0).index(); + nextPrev = $items.eq(next).parent().prevAll(':not(.disabled):visible').eq(0).index(); + + if (that.options.liveSearch) { + $items.each(function(i) { + if ($(this).is(':not(.disabled)')) { + $(this).data('index', i); + } + }); + index = $items.index($items.filter('.active')); + first = $items.filter(':not(.disabled):visible').first().data('index'); + last = $items.filter(':not(.disabled):visible').last().data('index'); + next = $items.eq(index).nextAll(':not(.disabled):visible').eq(0).data('index'); + prev = $items.eq(index).prevAll(':not(.disabled):visible').eq(0).data('index'); + nextPrev = $items.eq(next).prevAll(':not(.disabled):visible').eq(0).data('index'); + } + + prevIndex = $this.data('prevIndex'); + + if (e.keyCode == 38) { + if (that.options.liveSearch) index -= 1; + if (index != nextPrev && index > prev) index = prev; + if (index < first) index = first; + if (index == prevIndex) index = last; + } + + if (e.keyCode == 40) { + if (that.options.liveSearch) index += 1; + if (index == -1) index = 0; + if (index != nextPrev && index < next) index = next; + if (index > last) index = last; + if (index == prevIndex) index = first; + } + + $this.data('prevIndex', index); + + if (!that.options.liveSearch) { + $items.eq(index).focus(); + } else { + e.preventDefault(); + if (!$this.is('.dropdown-toggle')) { + $items.removeClass('active'); + $items.eq(index).addClass('active').find('a').focus(); + $this.focus(); + } + } + + } else if (!$this.is('input')) { + + var keyIndex = [], + count, + prevKey; + + $items.each(function() { + if ($(this).parent().is(':not(.disabled)')) { + if ($.trim($(this).text().toLowerCase()).substring(0,1) == keyCodeMap[e.keyCode]) { + keyIndex.push($(this).parent().index()); + } + } + }); + + count = $(document).data('keycount'); + count++; + $(document).data('keycount',count); + + prevKey = $.trim($(':focus').text().toLowerCase()).substring(0,1); + + if (prevKey != keyCodeMap[e.keyCode]) { + count = 1; + $(document).data('keycount', count); + } else if (count >= keyIndex.length) { + $(document).data('keycount', 0); + if (count > keyIndex.length) count = 1; + } + + $items.eq(keyIndex[count - 1]).focus(); + } + + // Select focused option if "Enter", "Spacebar", "Tab" are pressed inside the menu. + if (/(13|32|^9$)/.test(e.keyCode) && isActive) { + if (!/(32)/.test(e.keyCode)) e.preventDefault(); + if (!that.options.liveSearch) { + $(':focus').click(); + } else if (!/(32)/.test(e.keyCode)) { + that.$menu.find('.active a').click(); + $this.focus(); + } + $(document).data('keycount',0); + } + + if ((/(^9$|27)/.test(e.keyCode) && isActive && (that.multiple || that.options.liveSearch)) || (/(27)/.test(e.keyCode) && !isActive)) { + that.$menu.parent().removeClass('open'); + that.$button.focus(); + } + + }, + + hide: function() { + this.$newElement.hide(); + }, + + show: function() { + this.$newElement.show(); + }, + + destroy: function() { + this.$newElement.remove(); + this.$element.remove(); + } + }; + + $.fn.selectpicker = function(option, event) { + //get the args of the outer function.. + var args = arguments; + var value; + var chain = this.each(function() { + if ($(this).is('select')) { + var $this = $(this), + data = $this.data('selectpicker'), + options = typeof option == 'object' && option; + + if (!data) { + $this.data('selectpicker', (data = new Selectpicker(this, options, event))); + } else if (options) { + for(var i in options) { + data.options[i] = options[i]; + } + } + + if (typeof option == 'string') { + //Copy the value of option, as once we shift the arguments + //it also shifts the value of option. + var property = option; + if (data[property] instanceof Function) { + [].shift.apply(args); + value = data[property].apply(data, args); + } else { + value = data.options[property]; + } + } + } + }); + + if (value !== undefined) { + return value; + } else { + return chain; + } + }; + + $.fn.selectpicker.defaults = { + style: 'btn-default', + size: 'auto', + title: null, + selectedTextFormat : 'values', + noneSelectedText : 'Nothing selected', + noneResultsText : 'No results match', + countSelectedText: '{0} of {1} selected', + maxOptionsText: ['Limit reached ({n} {var} max)', 'Group limit reached ({n} {var} max)', ['items','item']], + width: false, + container: false, + hideDisabled: false, + showSubtext: false, + showIcon: true, + showContent: true, + dropupAuto: false, + header: false, + liveSearch: false, + actionsBox: false, + multipleSeparator: ', ', + iconBase: 'glyphicon', + tickIcon: 'glyphicon-ok', + maxOptions: false + }; + + $(document) + .data('keycount', 0) + .on('keydown', '.bootstrap-select [data-toggle=dropdown], .bootstrap-select [role=menu], .bootstrap-select-searchbox input', Selectpicker.prototype.keydown) + .on('focusin.modal', '.bootstrap-select [data-toggle=dropdown], .bootstrap-select [role=menu], .bootstrap-select-searchbox input', function (e) { e.stopPropagation(); }); + +}(window.jQuery); \ No newline at end of file diff --git a/tutor/vendor/assets/stylesheets/active_admin.css.scss b/tutor/vendor/assets/stylesheets/active_admin.css.scss new file mode 100644 index 00000000..90ba1d47 --- /dev/null +++ b/tutor/vendor/assets/stylesheets/active_admin.css.scss @@ -0,0 +1,17 @@ +// SASS variable overrides must be declared before loading up Active Admin's styles. +// +// To view the variables that Active Admin provides, take a look at +// `app/assets/stylesheets/active_admin/mixins/_variables.css.scss` in the +// Active Admin source. +// +// For example, to change the sidebar width: +// $sidebar-width: 242px; + +// Active Admin's got SASS! +@import "active_admin/mixins"; +@import "active_admin/base"; + +// Overriding any non-variable SASS must be done after the fact. +// For example, to change the default status-tag color: +// +// .status_tag { background: #6090DB; } diff --git a/tutor/vendor/assets/stylesheets/bootstrap.css b/tutor/vendor/assets/stylesheets/bootstrap.css index 21f91353..6e08fd9a 100644 --- a/tutor/vendor/assets/stylesheets/bootstrap.css +++ b/tutor/vendor/assets/stylesheets/bootstrap.css @@ -2431,8 +2431,11 @@ input[type="button"].btn-block { } @font-face { font-family: 'Glyphicons Halflings'; - src: url('../fonts/glyphicons-halflings-regular.eot'); - src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); + src: url('/assets/glyphicons-halflings-regular.eot'); + src: url('/assets/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), + url('/assets/glyphicons-halflings-regular.woff') format('woff'), + url('/assets/glyphicons-halflings-regular.ttf') format('truetype'), + url('/assets/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); } .glyphicon { position: relative; @@ -6010,3 +6013,304 @@ input:focus { -webkit-box-shadow: none; box-shadow: none; } +.bootstrap-select.btn-group:not(.input-group-btn), +.bootstrap-select.btn-group[class*="span"] { + float: none; + display: inline-block; + margin-bottom: 10px; + margin-left: 0; +} +.form-search .bootstrap-select.btn-group, +.form-inline .bootstrap-select.btn-group, +.form-horizontal .bootstrap-select.btn-group { + margin-bottom: 0; +} + +.bootstrap-select.form-control { + margin-bottom: 0; + padding: 0; + border: none; +} + +.bootstrap-select.btn-group.pull-right, +.bootstrap-select.btn-group[class*="span"].pull-right, +.row-fluid .bootstrap-select.btn-group[class*="span"].pull-right { + float: right; +} + +.input-append .bootstrap-select.btn-group { + margin-left: -1px; +} + +.input-prepend .bootstrap-select.btn-group { + margin-right: -1px; +} + +.bootstrap-select:not([class*="span"]):not([class*="col-"]):not([class*="form-control"]):not(.input-group-btn) { + width: 220px; +} + +.bootstrap-select { + /*width: 220px\9; IE8 and below*/ + width: 220px\0; /*IE9 and below*/ +} + +.bootstrap-select.form-control:not([class*="span"]) { + width: 100%; +} + +.bootstrap-select > .btn { + width: 100%; + padding-right: 25px; +} + +.error .bootstrap-select .btn { + border: 1px solid #b94a48; +} + +.bootstrap-select.show-menu-arrow.open > .btn { + z-index: 2051; +} + +.bootstrap-select .btn:focus { + outline: thin dotted #333333 !important; + outline: 5px auto -webkit-focus-ring-color !important; + outline-offset: -2px; +} + +.bootstrap-select.btn-group .btn .filter-option { + display: inline-block; + overflow: hidden; + width: 100%; + float: left; + text-align: left; +} + +.bootstrap-select.btn-group .btn .caret { + position: absolute; + top: 50%; + right: 12px; + margin-top: -2px; + vertical-align: middle; +} + +.bootstrap-select.btn-group > .disabled, +.bootstrap-select.btn-group .dropdown-menu li.disabled > a { + cursor: not-allowed; +} + +.bootstrap-select.btn-group > .disabled:focus { + outline: none !important; +} + +.bootstrap-select.btn-group[class*="span"] .btn { + width: 100%; +} + +.bootstrap-select.btn-group .dropdown-menu { + min-width: 100%; + z-index: 2000; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.bootstrap-select.btn-group .dropdown-menu.inner { + position: static; + border: 0; + padding: 0; + margin: 0; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} + +.bootstrap-select.btn-group .dropdown-menu dt { + display: block; + padding: 3px 20px; + cursor: default; +} + +.bootstrap-select.btn-group .div-contain { + overflow: hidden; +} + +.bootstrap-select.btn-group .dropdown-menu li { + position: relative; +} + +.bootstrap-select.btn-group .dropdown-menu li > a.opt { + position: relative; + padding-left: 35px; +} + +.bootstrap-select.btn-group .dropdown-menu li > a { + cursor: pointer; +} + +.bootstrap-select.btn-group .dropdown-menu li > dt small { + font-weight: normal; +} + +.bootstrap-select.btn-group.show-tick .dropdown-menu li.selected a i.check-mark { + position: absolute; + display: inline-block; + right: 15px; + margin-top: 2.5px; +} + +.bootstrap-select.btn-group .dropdown-menu li a i.check-mark { + display: none; +} + +.bootstrap-select.btn-group.show-tick .dropdown-menu li a span.text { + margin-right: 34px; +} + +.bootstrap-select.btn-group .dropdown-menu li small { + padding-left: 0.5em; +} + +.bootstrap-select.btn-group .dropdown-menu li:not(.disabled) > a:hover small, +.bootstrap-select.btn-group .dropdown-menu li:not(.disabled) > a:focus small, +.bootstrap-select.btn-group .dropdown-menu li.active:not(.disabled) > a small { + color: #64b1d8; + color: rgba(255,255,255,0.4); +} + +.bootstrap-select.btn-group .dropdown-menu li > dt small { + font-weight: normal; +} + +.bootstrap-select.show-menu-arrow .dropdown-toggle:before { + content: ''; + display: inline-block; + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-bottom: 7px solid #CCC; + border-bottom-color: rgba(0, 0, 0, 0.2); + position: absolute; + bottom: -4px; + left: 9px; + display: none; +} + +.bootstrap-select.show-menu-arrow .dropdown-toggle:after { + content: ''; + display: inline-block; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid white; + position: absolute; + bottom: -4px; + left: 10px; + display: none; +} + +.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle:before { + bottom: auto; + top: -3px; + border-top: 7px solid #ccc; + border-bottom: 0; + border-top-color: rgba(0, 0, 0, 0.2); +} + +.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle:after { + bottom: auto; + top: -3px; + border-top: 6px solid #ffffff; + border-bottom: 0; +} + +.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle:before { + right: 12px; + left: auto; +} +.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle:after { + right: 13px; + left: auto; +} + +.bootstrap-select.show-menu-arrow.open > .dropdown-toggle:before, +.bootstrap-select.show-menu-arrow.open > .dropdown-toggle:after { + display: block; +} + +.bootstrap-select.btn-group .no-results { + padding: 3px; + background: #f5f5f5; + margin: 0 5px; +} + +.bootstrap-select.btn-group .dropdown-menu .notify { + position: absolute; + bottom: 5px; + width: 96%; + margin: 0 2%; + min-height: 26px; + padding: 3px 5px; + background: #f5f5f5; + border: 1px solid #e3e3e3; + box-shadow: inset 0 1px 1px rgba(0,0,0,0.05); + pointer-events: none; + opacity: 0.9; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.mobile-device { + position: absolute; + top: 0; + left: 0; + display: block !important; + width: 100%; + height: 100% !important; + opacity: 0; +} + +.bootstrap-select.fit-width { + width: auto !important; +} + +.bootstrap-select.btn-group.fit-width .btn .filter-option { + position: static; +} + +.bootstrap-select.btn-group.fit-width .btn .caret { + position: static; + top: auto; + margin-top: -1px; +} + +.control-group.error .bootstrap-select .dropdown-toggle{ + border-color: #b94a48; +} + +.bootstrap-select-searchbox, +.bootstrap-select .bs-actionsbox { + padding: 4px 8px; +} + +.bootstrap-select .bs-actionsbox { + float: left; + width: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.bootstrap-select-searchbox + .bs-actionsbox { + padding: 0 8px 4px; +} + +.bootstrap-select-searchbox input { + margin-bottom: 0; +} + +.bootstrap-select .bs-actionsbox .btn-group button { + width: 50%; +}