Update Turbolinks, load additional assets only on request, fix JS
Signed-off-by: Sebastian Serth <Sebastian.Serth@student.hpi.de>
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -24,3 +24,4 @@
|
||||
/.vagrant
|
||||
*.iml
|
||||
*.DS_Store
|
||||
/node_modules
|
||||
|
3
Gemfile
3
Gemfile
@ -10,7 +10,6 @@ gem 'forgery'
|
||||
gem 'highline'
|
||||
gem 'jbuilder'
|
||||
gem 'jquery-rails'
|
||||
gem 'jquery-turbolinks'
|
||||
gem 'ims-lti', '< 2.0.0'
|
||||
gem 'kramdown'
|
||||
gem 'newrelic_rpm'
|
||||
@ -26,7 +25,7 @@ gem 'sass-rails'
|
||||
gem 'slim-rails'
|
||||
gem 'bootstrap_pagedown'
|
||||
gem 'sorcery'
|
||||
gem 'turbolinks', '< 5.0.0' # newer versions prevent loading ACE if the page containing is not accessed directly / refreshed
|
||||
gem 'turbolinks'
|
||||
gem 'uglifier'
|
||||
gem 'tubesock', git: 'https://github.com/gosukiwi/tubesock', branch: 'patch-1' # Switch to a fork which is compatible with Rails 5
|
||||
gem 'faye-websocket'
|
||||
|
28
Gemfile.lock
28
Gemfile.lock
@ -76,8 +76,8 @@ GEM
|
||||
bootstrap_pagedown (1.1.0)
|
||||
rails (>= 3.2)
|
||||
builder (3.2.3)
|
||||
bunny (2.11.0)
|
||||
amq-protocol (~> 2.3.0)
|
||||
bunny (2.12.0)
|
||||
amq-protocol (~> 2.3, >= 2.3.0)
|
||||
byebug (10.0.2)
|
||||
capistrano (3.11.0)
|
||||
airbrussh (>= 1.0.0)
|
||||
@ -99,7 +99,7 @@ GEM
|
||||
capistrano (~> 3.7)
|
||||
capistrano-bundler
|
||||
puma (~> 3.4)
|
||||
capybara (3.7.2)
|
||||
capybara (3.8.2)
|
||||
addressable
|
||||
mini_mime (>= 0.1.3)
|
||||
nokogiri (~> 1.8)
|
||||
@ -116,13 +116,6 @@ GEM
|
||||
codeclimate-test-reporter (1.0.8)
|
||||
simplecov (<= 0.13)
|
||||
coderay (1.1.2)
|
||||
coffee-rails (4.2.2)
|
||||
coffee-script (>= 2.2.0)
|
||||
railties (>= 4.0.0)
|
||||
coffee-script (2.4.1)
|
||||
coffee-script-source
|
||||
execjs
|
||||
coffee-script-source (1.12.2)
|
||||
concurrent-ruby (1.0.5)
|
||||
crass (1.0.4)
|
||||
d3-rails (5.5.0)
|
||||
@ -171,9 +164,6 @@ GEM
|
||||
rails-dom-testing (>= 1, < 3)
|
||||
railties (>= 4.2.0)
|
||||
thor (>= 0.14, < 2.0)
|
||||
jquery-turbolinks (2.1.0)
|
||||
railties (>= 3.1.0)
|
||||
turbolinks
|
||||
json (2.1.0)
|
||||
jwt (1.5.6)
|
||||
kramdown (1.17.0)
|
||||
@ -308,7 +298,7 @@ GEM
|
||||
rspec-mocks (~> 3.8.0)
|
||||
rspec-support (~> 3.8.0)
|
||||
rspec-support (3.8.0)
|
||||
rubocop (0.59.1)
|
||||
rubocop (0.59.2)
|
||||
jaro_winkler (~> 1.5.1)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 2.5, != 2.5.1.1)
|
||||
@ -324,7 +314,7 @@ GEM
|
||||
json (~> 2.1)
|
||||
structured_warnings (~> 0.3)
|
||||
rubyzip (1.2.2)
|
||||
sass (3.5.7)
|
||||
sass (3.6.0)
|
||||
sass-listen (~> 4.0.0)
|
||||
sass-listen (4.0.0)
|
||||
rb-fsevent (~> 0.9, >= 0.9.4)
|
||||
@ -371,8 +361,9 @@ GEM
|
||||
thor (0.20.0)
|
||||
thread_safe (0.3.6)
|
||||
tilt (2.0.8)
|
||||
turbolinks (2.5.4)
|
||||
coffee-rails
|
||||
turbolinks (5.2.0)
|
||||
turbolinks-source (~> 5.2)
|
||||
turbolinks-source (5.2.0)
|
||||
tzinfo (1.2.5)
|
||||
thread_safe (~> 0.1)
|
||||
uglifier (4.1.19)
|
||||
@ -427,7 +418,6 @@ DEPENDENCIES
|
||||
ims-lti (< 2.0.0)
|
||||
jbuilder
|
||||
jquery-rails
|
||||
jquery-turbolinks
|
||||
kramdown
|
||||
listen
|
||||
mnemosyne-ruby
|
||||
@ -457,7 +447,7 @@ DEPENDENCIES
|
||||
sorcery
|
||||
spring
|
||||
tubesock!
|
||||
turbolinks (< 5.0.0)
|
||||
turbolinks
|
||||
uglifier
|
||||
web-console
|
||||
whenever
|
||||
|
@ -11,11 +11,10 @@
|
||||
// about supported directives.
|
||||
//
|
||||
//= require jquery
|
||||
//= require jquery.turbolinks
|
||||
//= require jquery_ujs
|
||||
//= require turbolinks
|
||||
//= require bootstrap_pagedown
|
||||
//= require d3
|
||||
//= require turbolinks
|
||||
//
|
||||
// lib/assets
|
||||
//= require flash
|
||||
@ -29,5 +28,4 @@
|
||||
//= require chosen.jquery.min
|
||||
//= require jquery-ui.min
|
||||
//= require underscore.min
|
||||
//= require vis.min
|
||||
//= require_tree .
|
||||
|
@ -1,4 +1,4 @@
|
||||
$(document).ready(function () {
|
||||
$(document).on('turbolinks:load', function() {
|
||||
|
||||
var subMenusSelector = 'ul.dropdown-menu [data-toggle=dropdown]';
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
$(function() {
|
||||
$(document).on('turbolinks:load', function() {
|
||||
var CHART_START = window.vis ? vis.moment().add(-1, 'minute') : undefined;
|
||||
var DEFAULT_REFRESH_INTERVAL = 5000;
|
||||
|
||||
@ -51,7 +51,7 @@ $(function() {
|
||||
} else {
|
||||
var jqxhr = $.ajax({
|
||||
dataType: 'json',
|
||||
url: 'dashboard',
|
||||
url: '/admin/dashboard',
|
||||
method: 'GET'
|
||||
});
|
||||
jqxhr.done(function(response) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
$(function() {
|
||||
$(document).on('turbolinks:load', function() {
|
||||
|
||||
//Merge all editor components.
|
||||
$.extend(
|
@ -1,4 +1,4 @@
|
||||
$(function() {
|
||||
$(document).on('turbolinks:load', function() {
|
||||
if ($.isController('error_templates')) {
|
||||
$('#add-attribute').find('button').on('click', function () {
|
||||
$.ajax(location + '/attribute.json', {
|
||||
|
@ -1,4 +1,4 @@
|
||||
$(function() {
|
||||
$(document).on('turbolinks:load', function() {
|
||||
if ($.isController('execution_environments')) {
|
||||
if ($('.edit_execution_environment, .new_execution_environment').isPresent()) {
|
||||
new MarkdownEditor('#execution_environment_help');
|
||||
|
@ -1,4 +1,4 @@
|
||||
$(function() {
|
||||
$(document).on('turbolinks:load', function() {
|
||||
if ($.isController('exercise_collections')) {
|
||||
var dataElement = $('#data');
|
||||
var exerciseList = $('#exercise-list');
|
||||
|
@ -1,4 +1,4 @@
|
||||
$(function() {
|
||||
$(document).on('turbolinks:load', function() {
|
||||
// /exercises/38/statistics good for testing
|
||||
|
||||
if ($.isController('exercises') && $('.graph-functions-2').isPresent()) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
$(function() {
|
||||
$(document).on('turbolinks:load', function() {
|
||||
// ruby part adds the relative_url_root, if it is set.
|
||||
var ACE_FILES_PATH = '<%= (defined? Rails.application.config.relative_url_root) && Rails.application.config.relative_url_root != nil && Rails.application.config.relative_url_root != "" ? Rails.application.config.relative_url_root : "" %>' + '/assets/ace/';
|
||||
var THEME = 'ace/theme/textmate';
|
||||
|
@ -1,4 +1,4 @@
|
||||
$(function() {
|
||||
$(document).on('turbolinks:load', function() {
|
||||
var grid = $('#tag-grid');
|
||||
|
||||
if ($.isController('external_users') && grid.isPresent()) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
$(function() {
|
||||
$(document).on('turbolinks:load', function() {
|
||||
var CHOSEN_OPTIONS = {
|
||||
allow_single_deselect: true,
|
||||
disable_search_threshold: 5,
|
||||
|
@ -1,4 +1,4 @@
|
||||
$(function() {
|
||||
$(document).on('turbolinks:load', function() {
|
||||
var ENTER_KEY_CODE = 13;
|
||||
|
||||
var clearOutput = function() {
|
||||
|
@ -1,4 +1,4 @@
|
||||
$(document).ready(function () {
|
||||
$(document).on('turbolinks:load', function() {
|
||||
|
||||
function manageActivityHistory(prefix) {
|
||||
var containerId = prefix + '-activity-history';
|
||||
|
@ -1,4 +1,4 @@
|
||||
$(document).ready(function () {
|
||||
$(document).on('turbolinks:load', function() {
|
||||
if ($.isController('statistics') && $('.graph#user-activity').isPresent()) {
|
||||
|
||||
function manageGraph(containerId, url, refreshAfter) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
$(function() {
|
||||
$(document).on('turbolinks:load', function() {
|
||||
|
||||
var ACE_FILES_PATH = '/assets/ace/';
|
||||
var THEME = 'ace/theme/textmate';
|
||||
|
@ -1,4 +1,4 @@
|
||||
$(function() {
|
||||
$(document).on('turbolinks:load', function() {
|
||||
// http://localhost:3333/exercises/38/statistics good for testing
|
||||
// originally at--> localhost:3333/exercises/69/statistics
|
||||
|
||||
@ -30,7 +30,7 @@ $(function() {
|
||||
var studentTime = minutes_array[i];
|
||||
|
||||
for (var j = 0; j < studentTime; j++){
|
||||
if (minutes_count[j] == undefined){
|
||||
if (minutes_count[j] === undefined){
|
||||
minutes_count[j] = 0;
|
||||
} else{
|
||||
minutes_count[j] += 1;
|
||||
@ -173,23 +173,23 @@ $(function() {
|
||||
//}
|
||||
|
||||
// DRAW THE SECOND GRAPH ------------------------------------------------------------------------------
|
||||
//<script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"></script>
|
||||
function draw_bar_graph() {
|
||||
var group_incrament = 5;
|
||||
var group_ranges = group_incrament; // just for the start
|
||||
var number_of_bars = 40;
|
||||
var group_increment = Math.ceil(maximum_minutes / number_of_bars); // range in minutes
|
||||
var group_ranges = group_increment; // just for the start
|
||||
var minutes_array_for_bar = [];
|
||||
|
||||
do {
|
||||
var section_value = 0;
|
||||
for (var i = 0; i < minutes_array.length; i++) {
|
||||
if ((minutes_array[i] < group_ranges) && (minutes_array[i] >= (group_ranges - group_incrament))) {
|
||||
if ((minutes_array[i] < group_ranges) && (minutes_array[i] >= (group_ranges - group_increment))) {
|
||||
section_value++;
|
||||
}
|
||||
}
|
||||
minutes_array_for_bar.push(section_value);
|
||||
group_ranges += group_incrament;
|
||||
group_ranges += group_increment;
|
||||
}
|
||||
while (group_ranges < maximum_minutes + group_incrament);
|
||||
while (group_ranges < maximum_minutes + group_increment);
|
||||
|
||||
//console.log(minutes_array_for_bar); // this var used as the bars
|
||||
//minutes_array_for_bar = [39, 20, 28, 20, 39, 34, 26, 23, 16, 8];
|
||||
@ -199,30 +199,30 @@ $(function() {
|
||||
|
||||
|
||||
var width_ratio = .8;
|
||||
if (getWidth()*width_ratio > 1000){
|
||||
width_ratio = 1000/getWidth();
|
||||
}
|
||||
var height_ratio = .7; // percent of height
|
||||
|
||||
var margin = {top: 100, right: 20, bottom: 70, left: 70},//30,50
|
||||
width = (getWidth() * width_ratio) - margin.left - margin.right,
|
||||
height = (width * height_ratio) - margin.top - margin.bottom;
|
||||
|
||||
var x = d3.scale.ordinal()
|
||||
.rangeRoundBands([0, width], .1);
|
||||
var x = d3.scaleBand()
|
||||
.range([0, width], .1);
|
||||
|
||||
var y = d3.scaleLinear()
|
||||
.range([0,height-(margin.top + margin.bottom)]);
|
||||
|
||||
|
||||
var xAxis = d3.svg.axis()
|
||||
.scale(x)
|
||||
.orient("bottom")
|
||||
var xAxis = d3.axisBottom(x)
|
||||
.ticks(10);
|
||||
|
||||
|
||||
var yAxis = d3.svg.axis()
|
||||
.scale(d3.scaleLinear().domain([0,max_of_array]).range([height,0]))//y
|
||||
.orient("left")
|
||||
var yAxis = d3
|
||||
.axisLeft(d3.scaleLinear().domain([0,max_of_array]).range([height,0]))//y
|
||||
.ticks(10)
|
||||
.innerTickSize(-width);
|
||||
.tickSizeInner(-width);
|
||||
|
||||
var tip = d3.tip()
|
||||
.attr('class', 'd3-tip')
|
||||
@ -241,8 +241,8 @@ $(function() {
|
||||
|
||||
x.domain(minutes_array_for_bar.map(function (d, i) {
|
||||
i++;
|
||||
var high_side = i * group_incrament;
|
||||
var low_side = high_side - group_incrament;
|
||||
var high_side = i * group_increment;
|
||||
var low_side = high_side - group_increment;
|
||||
return (low_side+"-"+high_side);
|
||||
}));
|
||||
|
||||
@ -253,7 +253,14 @@ $(function() {
|
||||
svg.append("g")
|
||||
.attr("class", "x axis")
|
||||
.attr("transform", "translate(0," + height + ")")
|
||||
.call(xAxis);
|
||||
.call(xAxis)
|
||||
.selectAll("text")
|
||||
.style("text-anchor", "end")
|
||||
.attr("dx", "-.8em")
|
||||
.attr("dy", ".15em")
|
||||
.attr("transform", function(d) {
|
||||
return "rotate(-45)"
|
||||
});
|
||||
|
||||
svg.append("g")
|
||||
.attr("class", "y axis")
|
||||
@ -278,7 +285,7 @@ $(function() {
|
||||
.attr("text-anchor", "middle")
|
||||
.attr("x", width / 2)
|
||||
.attr("y", height)
|
||||
.attr("dy", ((height / 20) + 20) + 'px')
|
||||
.attr("dy", ((height / 20) + 40) + 'px')
|
||||
.text("Working Time (Minutes)")
|
||||
.style('font-size', 14);
|
||||
|
||||
@ -291,10 +298,10 @@ $(function() {
|
||||
.data(minutes_array_for_bar)
|
||||
.enter().append("rect")
|
||||
.attr("class", "bar")
|
||||
.attr("x", function(d,i) { var bar_incriment = width/ minutes_array_for_bar.length;
|
||||
var bar_x = i * bar_incriment;
|
||||
.attr("x", function(d,i) { var bar_increment = width / minutes_array_for_bar.length;
|
||||
var bar_x = i * bar_increment;
|
||||
return (bar_x)})
|
||||
.attr("width", x.rangeBand())
|
||||
.attr("width", x.bandwidth())
|
||||
.attr("y", function(d) { return height - y(d); })
|
||||
.attr("height", function(d) { return y(d); })
|
||||
.on('mouseover', tip.show)
|
||||
@ -311,7 +318,7 @@ $(function() {
|
||||
.style('text-decoration', 'underline');
|
||||
|
||||
}
|
||||
// draw_bar_graph();
|
||||
draw_bar_graph();
|
||||
}
|
||||
|
||||
});
|
||||
|
@ -23,6 +23,5 @@
|
||||
*= require jquery-ui.min
|
||||
*= require jquery-ui.structure.min
|
||||
*= require style.min
|
||||
*= require vis.min
|
||||
*= require_tree .
|
||||
*/
|
||||
|
@ -1,3 +1,10 @@
|
||||
- content_for :head do
|
||||
// Force a full page reload, see https://github.com/turbolinks/turbolinks/issues/326.
|
||||
Otherwise, the global variable `vis` might be uninitialized in the assets (race condition)
|
||||
meta name='turbolinks-visit-control' content='reload'
|
||||
= javascript_include_tag(asset_path('vis.min.js', type: :javascript))
|
||||
= stylesheet_link_tag(asset_path('vis.min.css', type: :stylesheet))
|
||||
|
||||
h1 = t('breadcrumbs.dashboard.show')
|
||||
|
||||
h2 Version
|
||||
|
@ -6,7 +6,7 @@
|
||||
span.caret
|
||||
ul.dropdown-menu role='menu'
|
||||
- if current_user.admin?
|
||||
li = link_to(t('breadcrumbs.dashboard.show'), admin_dashboard_path)
|
||||
li = link_to(t('breadcrumbs.dashboard.show'), admin_dashboard_path, 'data-turbolinks' => "false")
|
||||
li = link_to(t('breadcrumbs.statistics.show'), statistics_path)
|
||||
li.divider
|
||||
= render('navigation_submenu', title: t('activerecord.models.exercise.other'),
|
||||
|
@ -14,7 +14,7 @@ h1 = @execution_environment
|
||||
- if wts then average_time = wts["average_time"] else 0
|
||||
- if wts then stddev_time = wts["stddev_time"] else 0
|
||||
tr
|
||||
td = link_to exercise.title, controller: "exercises", action: "statistics", id: exercise.id
|
||||
td = link_to exercise.title, controller: "exercises", action: "statistics", id: exercise.id, 'data-turbolinks' => "false"
|
||||
td = us["users"]
|
||||
td = us["average_score"].to_f.round(4)
|
||||
td = us["maximum_score"].to_f.round(2)
|
||||
|
@ -23,7 +23,7 @@
|
||||
td
|
||||
span.fa.fa-bars
|
||||
td = item.exercise.title
|
||||
td = link_to(t('shared.show'), item.exercise)
|
||||
td = link_to(t('shared.show'), item.exercise, 'data-turbolinks' => "false")
|
||||
td
|
||||
a.remove-exercise href='#' = t('shared.destroy')
|
||||
.hidden
|
||||
|
@ -25,4 +25,4 @@ h4 = t('activerecord.attributes.exercise_collections.exercises')
|
||||
td = link_to(exercise.title, exercise)
|
||||
td = link_to_if(exercise.execution_environment && policy(exercise.execution_environment).show?, exercise.execution_environment, exercise.execution_environment)
|
||||
td = exercise.user.name
|
||||
td = link_to(t('shared.statistics'), statistics_exercise_path(exercise))
|
||||
td = link_to(t('shared.statistics'), statistics_exercise_path(exercise), 'data-turbolinks' => "false")
|
||||
|
@ -36,7 +36,7 @@ h1 = Exercise.model_name.human(count: 2)
|
||||
td.public data-value=exercise.public? = symbol_for(exercise.public?)
|
||||
td = link_to(t('shared.edit'), edit_exercise_path(exercise)) if policy(exercise).edit?
|
||||
td = link_to(t('.implement'), implement_exercise_path(exercise)) if policy(exercise).implement?
|
||||
td = link_to(t('shared.statistics'), statistics_exercise_path(exercise)) if policy(exercise).statistics?
|
||||
td = link_to(t('shared.statistics'), statistics_exercise_path(exercise), 'data-turbolinks' => "false") if policy(exercise).statistics?
|
||||
|
||||
td
|
||||
.btn-group
|
||||
@ -44,7 +44,7 @@ h1 = Exercise.model_name.human(count: 2)
|
||||
span.caret
|
||||
span.sr-only Toggle Dropdown
|
||||
ul.dropdown-menu.pull-right role="menu"
|
||||
li = link_to(t('shared.show'), exercise) if policy(exercise).show?
|
||||
li = link_to(t('shared.show'), exercise, 'data-turbolinks' => "false") if policy(exercise).show?
|
||||
li = link_to(t('activerecord.models.user_exercise_feedback.other'), feedback_exercise_path(exercise)) if policy(exercise).feedback?
|
||||
li = link_to(t('shared.destroy'), exercise, data: {confirm: t('shared.confirm_destroy')}, method: :delete) if policy(exercise).destroy?
|
||||
li = link_to(t('.clone'), clone_exercise_path(exercise), data: {confirm: t('shared.confirm_destroy')}, method: :post) if policy(exercise).clone?
|
||||
|
@ -1,6 +1,9 @@
|
||||
- content_for :head do
|
||||
= javascript_include_tag('http://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.4/highlight.min.js')
|
||||
= stylesheet_link_tag('http://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.4/styles/default.min.css')
|
||||
// Force a full page reload, see https://github.com/turbolinks/turbolinks/issues/326.
|
||||
Otherwise, code might not be highlighted correctly (race condition)
|
||||
meta name='turbolinks-visit-control' content='reload'
|
||||
= javascript_include_tag(asset_path('highlight.min.js', type: :javascript))
|
||||
= stylesheet_link_tag(asset_path('highlight-default.css', type: :stylesheet))
|
||||
|
||||
h1
|
||||
= @exercise
|
||||
|
@ -1,4 +1,8 @@
|
||||
script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"
|
||||
- content_for :head do
|
||||
// Force a full page reload, see https://github.com/turbolinks/turbolinks/issues/326.
|
||||
Otherwise, code might not be highlighted correctly (race condition)
|
||||
meta name='turbolinks-visit-control' content='reload'
|
||||
= javascript_include_tag(asset_path('d3-tip.js', type: :javascript))
|
||||
h1 = @exercise
|
||||
|
||||
= row(label: '.participants', value: @exercise.users.distinct.count)
|
||||
@ -32,8 +36,8 @@ h1 = @exercise
|
||||
.graph-functions
|
||||
div#chart_1
|
||||
hr
|
||||
/div#chart_2
|
||||
/hr
|
||||
div#chart_2
|
||||
hr
|
||||
.table-responsive
|
||||
table.table.table-striped.sortable
|
||||
thead
|
||||
|
@ -27,7 +27,7 @@ h1 = ProxyExercise.model_name.human(count: 2)
|
||||
span.caret
|
||||
span.sr-only Toggle Dropdown
|
||||
ul.dropdown-menu.pull-right role="menu"
|
||||
li = link_to(t('shared.show'), proxy_exercise) if policy(proxy_exercise).show?
|
||||
li = link_to(t('shared.show'), proxy_exercise, 'data-turbolinks' => "false") if policy(proxy_exercise).show?
|
||||
li = link_to(t('shared.destroy'), proxy_exercise, data: {confirm: t('shared.confirm_destroy')}, method: :delete) if policy(proxy_exercise).destroy?
|
||||
li = link_to(t('.clone'), clone_proxy_exercise_path(proxy_exercise), data: {confirm: t('shared.confirm_destroy')}, method: :post) if policy(proxy_exercise).clone?
|
||||
|
||||
|
@ -1,6 +1,9 @@
|
||||
- content_for :head do
|
||||
= javascript_include_tag('http://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.4/highlight.min.js')
|
||||
= stylesheet_link_tag('http://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.4/styles/default.min.css')
|
||||
// Force a full page reload, see https://github.com/turbolinks/turbolinks/issues/326.
|
||||
Otherwise, code might not be highlighted correctly (race condition)
|
||||
meta name='turbolinks-visit-control' content='reload'
|
||||
= javascript_include_tag(asset_path('highlight.min.js', type: :javascript))
|
||||
= stylesheet_link_tag(asset_path('highlight-default.css', type: :stylesheet))
|
||||
|
||||
h1
|
||||
= @proxy_exercise.title
|
||||
|
@ -1,3 +1,10 @@
|
||||
- content_for :head do
|
||||
// Force a full page reload, see https://github.com/turbolinks/turbolinks/issues/326.
|
||||
Otherwise, the global variable `vis` might be uninitialized in the assets (race condition)
|
||||
meta name='turbolinks-visit-control' content='reload'
|
||||
= javascript_include_tag(asset_path('vis.min.js', type: :javascript))
|
||||
= stylesheet_link_tag(asset_path('vis.min.css', type: :stylesheet))
|
||||
|
||||
.group
|
||||
.title
|
||||
h1 = t("statistics.graphs.#{resource}_activity")
|
||||
|
@ -1,3 +1,10 @@
|
||||
- content_for :head do
|
||||
// Force a full page reload, see https://github.com/turbolinks/turbolinks/issues/326.
|
||||
Otherwise, the global variable `vis` might be uninitialized in the assets (race condition)
|
||||
meta name='turbolinks-visit-control' content='reload'
|
||||
= javascript_include_tag(asset_path('vis.min.js', type: :javascript))
|
||||
= stylesheet_link_tag(asset_path('vis.min.css', type: :stylesheet))
|
||||
|
||||
.group
|
||||
.title
|
||||
h1 = t('.user_activity')
|
||||
|
@ -6,7 +6,7 @@ h1 = Submission.model_name.human(count: 2)
|
||||
= f.collection_select(:exercise_id_eq, Exercise.with_submissions, :id, :title, class: 'form-control', prompt: t('activerecord.attributes.submission.exercise'))
|
||||
.form-group
|
||||
= f.label(:cause_eq, t('activerecord.attributes.submission.cause'), class: 'sr-only')
|
||||
= f.select(:cause_eq, Submission.all.map(&:cause).uniq.sort, class: 'form-control', prompt: t('activerecord.attributes.submission.cause'))
|
||||
= f.select(:cause_eq, Submission.select(:cause).distinct.map(&:cause).sort, class: 'form-control', prompt: t('activerecord.attributes.submission.cause'))
|
||||
|
||||
.table-responsive
|
||||
table.table
|
||||
|
@ -5,6 +5,17 @@ Rails.application.config.tap do |config|
|
||||
# Version of your assets, change this if you want to expire all your assets.
|
||||
config.assets.version = '1.0'
|
||||
|
||||
# vis.js
|
||||
config.assets.precompile += %w( vis.min.js )
|
||||
config.assets.precompile += %w( vis.min.css )
|
||||
|
||||
# Highlight.js
|
||||
config.assets.precompile += %w( highlight.min.js )
|
||||
config.assets.precompile += %w( highlight-default.min.css )
|
||||
|
||||
# d3.tip
|
||||
config.assets.precompile += %w( d3-tip.js )
|
||||
|
||||
# Add additional assets to the asset load path.
|
||||
# config.assets.paths << Emoji.images_path
|
||||
# Add Yarn node_modules folder to the asset load path.
|
||||
|
@ -82,7 +82,7 @@ class DockerClient
|
||||
Rails.logger.debug "Opening Websocket on URL " + socket_url
|
||||
|
||||
socket.on :error do |event|
|
||||
Rails.logger.info "Websocket error: " + event.message
|
||||
Rails.logger.info "Websocket error: " + event.message.to_s
|
||||
end
|
||||
socket.on :close do |event|
|
||||
Rails.logger.info "Websocket closed."
|
||||
|
@ -19,13 +19,18 @@ sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 561F9B9CA
|
||||
sudo apt-get -qq -y install apt-transport-https ca-certificates
|
||||
sudo sh -c 'echo deb https://oss-binaries.phusionpassenger.com/apt/passenger trusty main > /etc/apt/sources.list.d/passenger.list'
|
||||
|
||||
# yarn & node
|
||||
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
|
||||
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
|
||||
curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
|
||||
|
||||
# rails
|
||||
sudo add-apt-repository -y ppa:chris-lea/node.js
|
||||
|
||||
sudo apt-get -qq update
|
||||
|
||||
# code_ocean
|
||||
sudo apt-get -qq -y install postgresql-client postgresql-$postgres_version postgresql-server-dev-$postgres_version vagrant
|
||||
sudo apt-get -qq -y install postgresql-client postgresql-$postgres_version postgresql-server-dev-$postgres_version vagrant yarn nodejs
|
||||
|
||||
# Docker
|
||||
if [ ! -f /etc/default/docker ]
|
||||
|
338
vendor/assets/javascripts/d3-tip.js
vendored
Normal file
338
vendor/assets/javascripts/d3-tip.js
vendored
Normal file
@ -0,0 +1,338 @@
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('d3-collection'), require('d3-selection')) :
|
||||
typeof define === 'function' && define.amd ? define(['d3-collection', 'd3-selection'], factory) :
|
||||
(global.d3 = global.d3 || {}, global.d3.tip = factory(global.d3,global.d3));
|
||||
}(this, (function (d3Collection,d3Selection) { 'use strict';
|
||||
|
||||
/**
|
||||
* d3.tip
|
||||
* Copyright (c) 2013-2017 Justin Palmer
|
||||
*
|
||||
* Tooltips for d3.js SVG visualizations
|
||||
*/
|
||||
// Public - constructs a new tooltip
|
||||
//
|
||||
// Returns a tip
|
||||
function index() {
|
||||
var direction = d3TipDirection,
|
||||
offset = d3TipOffset,
|
||||
html = d3TipHTML,
|
||||
rootElement = document.body,
|
||||
node = initNode(),
|
||||
svg = null,
|
||||
point = null,
|
||||
target = null;
|
||||
|
||||
function tip(vis) {
|
||||
svg = getSVGNode(vis);
|
||||
if (!svg) return
|
||||
point = svg.createSVGPoint();
|
||||
rootElement.appendChild(node);
|
||||
}
|
||||
|
||||
// Public - show the tooltip on the screen
|
||||
//
|
||||
// Returns a tip
|
||||
tip.show = function() {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
if (args[args.length - 1] instanceof SVGElement) target = args.pop();
|
||||
|
||||
var content = html.apply(this, args),
|
||||
poffset = offset.apply(this, args),
|
||||
dir = direction.apply(this, args),
|
||||
nodel = getNodeEl(),
|
||||
i = directions.length,
|
||||
coords,
|
||||
scrollTop = document.documentElement.scrollTop ||
|
||||
rootElement.scrollTop,
|
||||
scrollLeft = document.documentElement.scrollLeft ||
|
||||
rootElement.scrollLeft;
|
||||
|
||||
nodel.html(content)
|
||||
.style('opacity', 1).style('pointer-events', 'all');
|
||||
|
||||
while (i--) nodel.classed(directions[i], false);
|
||||
coords = directionCallbacks.get(dir).apply(this);
|
||||
nodel.classed(dir, true)
|
||||
.style('top', (coords.top + poffset[0]) + scrollTop + 'px')
|
||||
.style('left', (coords.left + poffset[1]) + scrollLeft + 'px');
|
||||
|
||||
return tip
|
||||
};
|
||||
|
||||
// Public - hide the tooltip
|
||||
//
|
||||
// Returns a tip
|
||||
tip.hide = function() {
|
||||
var nodel = getNodeEl();
|
||||
nodel.style('opacity', 0).style('pointer-events', 'none');
|
||||
return tip
|
||||
};
|
||||
|
||||
// Public: Proxy attr calls to the d3 tip container.
|
||||
// Sets or gets attribute value.
|
||||
//
|
||||
// n - name of the attribute
|
||||
// v - value of the attribute
|
||||
//
|
||||
// Returns tip or attribute value
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
tip.attr = function(n, v) {
|
||||
if (arguments.length < 2 && typeof n === 'string') {
|
||||
return getNodeEl().attr(n)
|
||||
}
|
||||
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
d3Selection.selection.prototype.attr.apply(getNodeEl(), args);
|
||||
return tip
|
||||
};
|
||||
|
||||
// Public: Proxy style calls to the d3 tip container.
|
||||
// Sets or gets a style value.
|
||||
//
|
||||
// n - name of the property
|
||||
// v - value of the property
|
||||
//
|
||||
// Returns tip or style property value
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
tip.style = function(n, v) {
|
||||
if (arguments.length < 2 && typeof n === 'string') {
|
||||
return getNodeEl().style(n)
|
||||
}
|
||||
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
d3Selection.selection.prototype.style.apply(getNodeEl(), args);
|
||||
return tip
|
||||
};
|
||||
|
||||
// Public: Set or get the direction of the tooltip
|
||||
//
|
||||
// v - One of n(north), s(south), e(east), or w(west), nw(northwest),
|
||||
// sw(southwest), ne(northeast) or se(southeast)
|
||||
//
|
||||
// Returns tip or direction
|
||||
tip.direction = function(v) {
|
||||
if (!arguments.length) return direction
|
||||
direction = v == null ? v : functor(v);
|
||||
|
||||
return tip
|
||||
};
|
||||
|
||||
// Public: Sets or gets the offset of the tip
|
||||
//
|
||||
// v - Array of [x, y] offset
|
||||
//
|
||||
// Returns offset or
|
||||
tip.offset = function(v) {
|
||||
if (!arguments.length) return offset
|
||||
offset = v == null ? v : functor(v);
|
||||
|
||||
return tip
|
||||
};
|
||||
|
||||
// Public: sets or gets the html value of the tooltip
|
||||
//
|
||||
// v - String value of the tip
|
||||
//
|
||||
// Returns html value or tip
|
||||
tip.html = function(v) {
|
||||
if (!arguments.length) return html
|
||||
html = v == null ? v : functor(v);
|
||||
|
||||
return tip
|
||||
};
|
||||
|
||||
// Public: sets or gets the root element anchor of the tooltip
|
||||
//
|
||||
// v - root element of the tooltip
|
||||
//
|
||||
// Returns root node of tip
|
||||
tip.rootElement = function(v) {
|
||||
if (!arguments.length) return rootElement
|
||||
rootElement = v == null ? v : functor(v);
|
||||
|
||||
return tip
|
||||
};
|
||||
|
||||
// Public: destroys the tooltip and removes it from the DOM
|
||||
//
|
||||
// Returns a tip
|
||||
tip.destroy = function() {
|
||||
if (node) {
|
||||
getNodeEl().remove();
|
||||
node = null;
|
||||
}
|
||||
return tip
|
||||
};
|
||||
|
||||
function d3TipDirection() { return 'n' }
|
||||
function d3TipOffset() { return [0, 0] }
|
||||
function d3TipHTML() { return ' ' }
|
||||
|
||||
var directionCallbacks = d3Collection.map({
|
||||
n: directionNorth,
|
||||
s: directionSouth,
|
||||
e: directionEast,
|
||||
w: directionWest,
|
||||
nw: directionNorthWest,
|
||||
ne: directionNorthEast,
|
||||
sw: directionSouthWest,
|
||||
se: directionSouthEast
|
||||
}),
|
||||
directions = directionCallbacks.keys();
|
||||
|
||||
function directionNorth() {
|
||||
var bbox = getScreenBBox(this);
|
||||
return {
|
||||
top: bbox.n.y - node.offsetHeight,
|
||||
left: bbox.n.x - node.offsetWidth / 2
|
||||
}
|
||||
}
|
||||
|
||||
function directionSouth() {
|
||||
var bbox = getScreenBBox(this);
|
||||
return {
|
||||
top: bbox.s.y,
|
||||
left: bbox.s.x - node.offsetWidth / 2
|
||||
}
|
||||
}
|
||||
|
||||
function directionEast() {
|
||||
var bbox = getScreenBBox(this);
|
||||
return {
|
||||
top: bbox.e.y - node.offsetHeight / 2,
|
||||
left: bbox.e.x
|
||||
}
|
||||
}
|
||||
|
||||
function directionWest() {
|
||||
var bbox = getScreenBBox(this);
|
||||
return {
|
||||
top: bbox.w.y - node.offsetHeight / 2,
|
||||
left: bbox.w.x - node.offsetWidth
|
||||
}
|
||||
}
|
||||
|
||||
function directionNorthWest() {
|
||||
var bbox = getScreenBBox(this);
|
||||
return {
|
||||
top: bbox.nw.y - node.offsetHeight,
|
||||
left: bbox.nw.x - node.offsetWidth
|
||||
}
|
||||
}
|
||||
|
||||
function directionNorthEast() {
|
||||
var bbox = getScreenBBox(this);
|
||||
return {
|
||||
top: bbox.ne.y - node.offsetHeight,
|
||||
left: bbox.ne.x
|
||||
}
|
||||
}
|
||||
|
||||
function directionSouthWest() {
|
||||
var bbox = getScreenBBox(this);
|
||||
return {
|
||||
top: bbox.sw.y,
|
||||
left: bbox.sw.x - node.offsetWidth
|
||||
}
|
||||
}
|
||||
|
||||
function directionSouthEast() {
|
||||
var bbox = getScreenBBox(this);
|
||||
return {
|
||||
top: bbox.se.y,
|
||||
left: bbox.se.x
|
||||
}
|
||||
}
|
||||
|
||||
function initNode() {
|
||||
var div = d3Selection.select(document.createElement('div'));
|
||||
div
|
||||
.style('position', 'absolute')
|
||||
.style('top', 0)
|
||||
.style('opacity', 0)
|
||||
.style('pointer-events', 'none')
|
||||
.style('box-sizing', 'border-box');
|
||||
|
||||
return div.node()
|
||||
}
|
||||
|
||||
function getSVGNode(element) {
|
||||
var svgNode = element.node();
|
||||
if (!svgNode) return null
|
||||
if (svgNode.tagName.toLowerCase() === 'svg') return svgNode
|
||||
return svgNode.ownerSVGElement
|
||||
}
|
||||
|
||||
function getNodeEl() {
|
||||
if (node == null) {
|
||||
node = initNode();
|
||||
// re-add node to DOM
|
||||
rootElement.appendChild(node);
|
||||
}
|
||||
return d3Selection.select(node)
|
||||
}
|
||||
|
||||
// Private - gets the screen coordinates of a shape
|
||||
//
|
||||
// Given a shape on the screen, will return an SVGPoint for the directions
|
||||
// n(north), s(south), e(east), w(west), ne(northeast), se(southeast),
|
||||
// nw(northwest), sw(southwest).
|
||||
//
|
||||
// +-+-+
|
||||
// | |
|
||||
// + +
|
||||
// | |
|
||||
// +-+-+
|
||||
//
|
||||
// Returns an Object {n, s, e, w, nw, sw, ne, se}
|
||||
function getScreenBBox(targetShape) {
|
||||
var targetel = target || targetShape;
|
||||
|
||||
while (targetel.getScreenCTM == null && targetel.parentNode != null) {
|
||||
targetel = targetel.parentNode;
|
||||
}
|
||||
|
||||
var bbox = {},
|
||||
matrix = targetel.getScreenCTM(),
|
||||
tbbox = targetel.getBBox(),
|
||||
width = tbbox.width,
|
||||
height = tbbox.height,
|
||||
x = tbbox.x,
|
||||
y = tbbox.y;
|
||||
|
||||
point.x = x;
|
||||
point.y = y;
|
||||
bbox.nw = point.matrixTransform(matrix);
|
||||
point.x += width;
|
||||
bbox.ne = point.matrixTransform(matrix);
|
||||
point.y += height;
|
||||
bbox.se = point.matrixTransform(matrix);
|
||||
point.x -= width;
|
||||
bbox.sw = point.matrixTransform(matrix);
|
||||
point.y -= height / 2;
|
||||
bbox.w = point.matrixTransform(matrix);
|
||||
point.x += width;
|
||||
bbox.e = point.matrixTransform(matrix);
|
||||
point.x -= width / 2;
|
||||
point.y -= height / 2;
|
||||
bbox.n = point.matrixTransform(matrix);
|
||||
point.y += height;
|
||||
bbox.s = point.matrixTransform(matrix);
|
||||
|
||||
return bbox
|
||||
}
|
||||
|
||||
// Private - replace D3JS 3.X d3.functor() function
|
||||
function functor(v) {
|
||||
return typeof v === 'function' ? v : function() {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
return tip
|
||||
}
|
||||
|
||||
return index;
|
||||
|
||||
})));
|
2
vendor/assets/javascripts/highlight.min.js
vendored
Normal file
2
vendor/assets/javascripts/highlight.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
99
vendor/assets/stylesheets/highlight-default.css
vendored
Normal file
99
vendor/assets/stylesheets/highlight-default.css
vendored
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
|
||||
Original highlight.js style (c) Ivan Sagalaev <maniac@softwaremaniacs.org>
|
||||
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 0.5em;
|
||||
background: #F0F0F0;
|
||||
}
|
||||
|
||||
|
||||
/* Base color: saturation 0; */
|
||||
|
||||
.hljs,
|
||||
.hljs-subst {
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.hljs-comment {
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-attribute,
|
||||
.hljs-selector-tag,
|
||||
.hljs-meta-keyword,
|
||||
.hljs-doctag,
|
||||
.hljs-name {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
/* User color: hue: 0 */
|
||||
|
||||
.hljs-type,
|
||||
.hljs-string,
|
||||
.hljs-number,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class,
|
||||
.hljs-quote,
|
||||
.hljs-template-tag,
|
||||
.hljs-deletion {
|
||||
color: #880000;
|
||||
}
|
||||
|
||||
.hljs-title,
|
||||
.hljs-section {
|
||||
color: #880000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-regexp,
|
||||
.hljs-symbol,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-link,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo {
|
||||
color: #BC6060;
|
||||
}
|
||||
|
||||
|
||||
/* Language color: hue: 90; */
|
||||
|
||||
.hljs-literal {
|
||||
color: #78A960;
|
||||
}
|
||||
|
||||
.hljs-built_in,
|
||||
.hljs-bullet,
|
||||
.hljs-code,
|
||||
.hljs-addition {
|
||||
color: #397300;
|
||||
}
|
||||
|
||||
|
||||
/* Meta color: hue: 200 */
|
||||
|
||||
.hljs-meta {
|
||||
color: #1f7199;
|
||||
}
|
||||
|
||||
.hljs-meta-string {
|
||||
color: #4d99bf;
|
||||
}
|
||||
|
||||
|
||||
/* Misc effects */
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
Reference in New Issue
Block a user