Commit 7361194d authored by Zac Wood's avatar Zac Wood

The Webpackening

parent 758c4ec2
Pipeline #3510 failed with stages
in 2 minutes and 2 seconds
{
"presets": [
["env", {
"modules": false,
"targets": {
"browsers": "> 1%",
"uglify": true
},
"useBuiltIns": true
}]
],
"plugins": [
"syntax-dynamic-import",
"transform-object-rest-spread",
["transform-class-properties", { "spec": true }]
]
}
......@@ -21,3 +21,8 @@
/yarn-error.log
.byebug_history
/public/packs
/public/packs-test
/node_modules
yarn-debug.log*
.yarn-integrity
plugins:
postcss-import: {}
postcss-cssnext: {}
......@@ -19,6 +19,8 @@ gem 'jbuilder', '~> 2.5'
gem 'uglifier'
gem 'webpacker', '~> 3.5'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 4.0'
# Use ActiveModel has_secure_password
......
......@@ -57,7 +57,7 @@ GEM
childprocess (0.9.0)
ffi (~> 1.0, >= 1.0.11)
coderay (1.1.2)
concurrent-ruby (1.1.3)
concurrent-ruby (1.1.4)
crass (1.0.4)
erubi (1.7.1)
execjs (2.7.0)
......@@ -101,7 +101,6 @@ GEM
parallel (1.12.1)
parser (2.5.3.0)
ast (~> 2.4.0)
pg (1.1.3)
powerpack (0.1.2)
pry (0.12.2)
coderay (~> 1.1.0)
......@@ -113,6 +112,8 @@ GEM
puma (3.12.0)
rack (2.0.6)
rack-cors (1.0.2)
rack-proxy (0.6.5)
rack
rack-test (1.1.0)
rack (>= 1.0, < 3)
rails (5.1.6.1)
......@@ -197,6 +198,10 @@ GEM
activemodel (>= 5.0)
bindex (>= 0.4.0)
railties (>= 5.0)
webpacker (3.5.5)
activesupport (>= 4.2)
rack-proxy (>= 0.6.1)
railties (>= 4.2)
websocket-driver (0.6.5)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.3)
......@@ -217,7 +222,6 @@ DEPENDENCIES
listen (>= 3.0.5, < 3.2)
maruku
nokogiri
pg
pry
pry-doc
puma (~> 3.7)
......@@ -233,6 +237,7 @@ DEPENDENCIES
tzinfo-data
uglifier
web-console (>= 3.3.0)
webpacker (~> 3.5)
BUNDLED WITH
1.16.3
/**
* Either adds or removes a section from the cart depending on
* if it is currently in the cart.
*/
const addOrRemoveFromCart = async (event, sectionNode) => {
event && event.stopPropagation();
const section = { ...sectionNode.dataset };
await this.cart.addSection(section);
if (this.cart.includesSection(section)) {
sectionNode.classList.add('selected');
} else {
sectionNode.classList.remove('selected');
}
};
const initListeners = () => {
const items = Array.from(document.querySelectorAll('.section-item'));
items.forEach(item => (item.onclick = e => addOrRemoveFromCart(e, item)));
};
document.addEventListener('DOMContentLoaded', initListeners);
// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.
// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.
{
"compilerOptions": {
"lib": ["es2015", "dom"]
}
}
// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.
......@@ -21,7 +21,7 @@ class SchedulesController < ApplicationController
@all = params[:crns].split(',').map { |crn|
CourseSection.latest_by_crn(crn)
}
@all.reject! { |s| s.nil? }
@all.reject!(&:nil?)
@without_online = @all.reject { |s|
s.start_time == "TBA" || s.end_time == "TBA"
}
......
//= require FileSaver
//= require cart
/* eslint no-console:0 */
// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.
//
// To reference this file, add <%= javascript_pack_tag 'application' %> to the appropriate
// layout file, like app/views/layouts/application.html.erb
import '@babel/polyfill';
import 'url-polyfill';
const elementFromString = string => {
const html = new DOMParser().parseFromString(string, 'text/html');
......@@ -7,7 +16,6 @@ const elementFromString = string => {
};
document.addEventListener('DOMContentLoaded', () => {
this.cart = new Cart();
initGlobalListeners();
});
......
import Cart from 'src/cart';
import { saveAs } from 'file-saver';
import html2canvas from 'html2canvas';
import $ from 'jquery';
import 'fullcalendar';
import 'moment';
document.addEventListener('DOMContentLoaded', () => {
const eventsTemplate = document.querySelector('#events');
if (eventsTemplate) {
......@@ -13,7 +20,6 @@ document.addEventListener('DOMContentLoaded', () => {
allDaySlot: false,
});
}
initListeners();
});
......@@ -22,7 +28,7 @@ const renderEvents = (start, end, timezone, callback) => {
};
const remove = async item => {
await window.cart.toggleSection({ ...item.dataset });
await Cart.toggleSection({ ...item.dataset });
location.reload(true);
};
......@@ -31,22 +37,22 @@ const remove = async item => {
* and sets the link in the modal to it.
*/
const setUrlInModal = () => {
document.getElementById('calendar-link').innerText = `${window.location.protocol}//${window.location.hostname}/api/schedules?crns=${window.cart._courses.join(',')}`;
document.getElementById('calendar-link').innerText = `${window.location.protocol}//${window.location.hostname}/api/schedules?crns=${Cart._courses.join(',')}`;
};
const downloadIcs = async () => {
const response = await fetch(`${window.location.protocol}//${window.location.hostname}/api/schedules?crns=${window.cart._courses.join(',')}`);
const response = await fetch(`${window.location.protocol}//${window.location.hostname}/api/schedules?crns=${Cart._courses.join(',')}`);
const text = await response.text();
const blob = new Blob([text], { type: 'text/calendar;charset=utf-8' });
saveAs(blob, 'GMU Schedule.ics');
};
const addToSystemCalendar = () => {
window.open(`webcal://${window.location.hostname}/api/schedules?crns=${window.cart._courses.join(',')}`);
window.open(`webcal://${window.location.hostname}/api/schedules?crns=${Cart._courses.join(',')}`);
};
const saveImage = () => {
html2canvas(document.querySelector("#calendar")).then(canvas => {
html2canvas(document.querySelector('#calendar')).then(canvas => {
canvas.toBlob(blob => {
saveAs(blob, 'GMU Schedule.png');
});
......@@ -62,5 +68,24 @@ const initListeners = () => {
document.getElementById('add-to-system').onclick = addToSystemCalendar;
document.getElementById('save-image').onclick = saveImage;
document.getElementById('share-url').innerText = `${window.location.protocol}//${window.location.hostname}/schedule/view?crns=${window.cart._courses.join(',')}`;
document.getElementById('share-url').innerText = `${window.location.protocol}//${window.location.hostname}/schedule/view?crns=${Cart._courses.join(',')}`;
};
if (!HTMLCanvasElement.prototype.toBlob) {
Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {
value: function(callback, type, quality) {
var canvas = this;
setTimeout(function() {
var binStr = atob(canvas.toDataURL(type, quality).split(',')[1]),
len = binStr.length,
arr = new Uint8Array(len);
for (var i = 0; i < len; i++) {
arr[i] = binStr.charCodeAt(i);
}
callback(new Blob([arr], { type: type || 'image/png' }));
});
},
});
}
import Cart from 'src/cart';
import { saveAs } from 'file-saver';
import html2canvas from 'html2canvas';
import $ from 'jquery';
import 'fullcalendar';
import 'moment';
import 'url-polyfill';
const params = new URLSearchParams(document.location.search);
const crns = params.get('crns');
......@@ -24,7 +32,6 @@ const renderEvents = (start, end, timezone, callback) => {
callback(window.events);
};
/**
* Generates a URL for the current sections in the schedule
* and sets the link in the modal to it.
......@@ -45,7 +52,7 @@ const addToSystemCalendar = () => {
};
const saveImage = () => {
html2canvas(document.querySelector("#calendar")).then(canvas => {
html2canvas(document.querySelector('#calendar')).then(canvas => {
canvas.toBlob(blob => {
saveAs(blob, 'GMU Schedule.png');
});
......@@ -56,6 +63,24 @@ const initListeners = () => {
document.getElementById('open-modal-btn').onclick = setUrlInModal;
document.getElementById('download-ics').onclick = downloadIcs;
document.getElementById('add-to-system').onclick = addToSystemCalendar;
document.getElementById('share-url').innerText = `${window.location.protocol}//${window.location.hostname}/schedule/view?crns=${crns}`;
document.getElementById('save-image').onclick = saveImage;
};
if (!HTMLCanvasElement.prototype.toBlob) {
Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {
value: function(callback, type, quality) {
var canvas = this;
setTimeout(function() {
var binStr = atob(canvas.toDataURL(type, quality).split(',')[1]),
len = binStr.length,
arr = new Uint8Array(len);
for (var i = 0; i < len; i++) {
arr[i] = binStr.charCodeAt(i);
}
callback(new Blob([arr], { type: type || 'image/png' }));
});
},
});
}
// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.
import Cart from 'src/cart';
/**
* Either adds or removes a section from the cart depending on
......@@ -9,10 +10,10 @@ const addOrRemoveFromCart = async (event, sectionNode) => {
event && event.stopPropagation();
const section = { ...sectionNode.dataset };
await this.cart.toggleSection(section);
await Cart.toggleSection(section);
const icon = $(sectionNode.querySelector('.add-remove-btn #icon'));
const text = sectionNode.querySelector('.add-remove-btn .text');
if (this.cart.includesSection(section)) {
if (Cart.includesSection(section)) {
icon.addClass('fa-minus').removeClass('fa-plus');
text.innerText = 'Remove';
} else {
......
//import '@babel/polyfill';
class Cart {
constructor() {
this.isOpen = false;
......@@ -9,6 +11,13 @@ class Cart {
}
}
_parseData() {
const cartData = document.getElementById('cart-data');
if (cartData) {
this._courses = JSON.parse(cartData.dataset.cart);
}
}
toggle() {
const list = document.getElementById('cart');
const icon = document.getElementById('schedule-icon');
......@@ -33,7 +42,10 @@ class Cart {
}
async toggleSection(section) {
const resp = await fetch(`/sessions/cart?&crn=${section.crn}`, { cache: 'no-store' });
const resp = await fetch(`/sessions/cart?&crn=${section.crn}`, {
cache: 'no-store',
credentials: 'same-origin'
});
const json = await resp.json();
this.courses = json;
}
......@@ -47,3 +59,10 @@ class Cart {
return false;
}
}
const cart = new Cart();
document.addEventListener('DOMContentLoaded', () => cart._parseData());
export default cart;
......@@ -29,5 +29,5 @@
</div>
<%= javascript_include_tag 'search' %>
<%= javascript_pack_tag 'search' %>
<%= stylesheet_link_tag 'search' %>
......@@ -21,5 +21,5 @@
</div>
</div>
<%= javascript_include_tag 'search' %>
<%= javascript_pack_tag 'search' %>
<%= stylesheet_link_tag 'search' %>
......@@ -6,11 +6,12 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<%= javascript_include_tag 'masonstrap.min' %>
<%= javascript_include_tag 'application' %>
<%= stylesheet_link_tag 'masonstrap.min' %>
<%= stylesheet_link_tag 'application' %>
<%= javascript_pack_tag 'application' %>
<!-- FB/Opengraph tags -->
<meta property="og:url" content="https://schedules.gmu.edu/">
<meta property="og:type" content="website">
......
<%= javascript_include_tag 'html2canvas.min' %>
<%= javascript_include_tag 'schedules' %>
<%= javascript_pack_tag 'schedules' %>
<%= stylesheet_link_tag 'schedules' %>
<%= javascript_include_tag 'moment.min' %>
<%= javascript_include_tag 'FileSaver' %>
<%= javascript_include_tag 'fullcalendar.min'%>
<%= stylesheet_link_tag 'fullcalendar.min' %>
......
<%= javascript_include_tag 'html2canvas.min' %>
<%= javascript_include_tag 'schedules_view' %>
<%= javascript_pack_tag 'schedules_view' %>
<%= stylesheet_link_tag 'schedules' %>
<%= javascript_include_tag 'moment.min' %>
<%= javascript_include_tag 'FileSaver' %>
<%= javascript_include_tag 'fullcalendar.min'%>
<%= stylesheet_link_tag 'fullcalendar.min' %>
......
......@@ -21,5 +21,5 @@
<% end %>
<%= javascript_include_tag 'search' %>
<%= javascript_pack_tag 'search' %>
<%= stylesheet_link_tag 'search' %>
#!/usr/bin/env ruby
ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
ENV["NODE_ENV"] ||= "development"
require "pathname"
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
Pathname.new(__FILE__).realpath)
require "rubygems"
require "bundler/setup"
require "webpacker"
require "webpacker/webpack_runner"
Webpacker::WebpackRunner.run(ARGV)
#!/usr/bin/env ruby
ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
ENV["NODE_ENV"] ||= "development"
require "pathname"
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
Pathname.new(__FILE__).realpath)
require "rubygems"
require "bundler/setup"
require "webpacker"
require "webpacker/dev_server_runner"
Webpacker::DevServerRunner.run(ARGV)
Rails.application.configure do
# Verifies that versions and hashed value of the package contents in the project's package.json
config.webpacker.check_yarn_integrity = true
# Settings specified here will take precedence over those in config/application.rb.
# In the development environment your application's code is reloaded on
......
Rails.application.configure do
# Verifies that versions and hashed value of the package contents in the project's package.json
config.webpacker.check_yarn_integrity = false
# Settings specified here will take precedence over those in config/application.rb.
# Code is not reloaded between requests.
......
process.env.NODE_ENV = process.env.NODE_ENV || 'development'
const environment = require('./environment')
module.exports = environment.toWebpackConfig()
const { environment } = require('@rails/webpacker')
module.exports = environment
process.env.NODE_ENV = process.env.NODE_ENV || 'production'
const environment = require('./environment')
module.exports = environment.toWebpackConfig()
process.env.NODE_ENV = process.env.NODE_ENV || 'development'
const environment = require('./environment')
module.exports = environment.toWebpackConfig()
# Note: You must restart bin/webpack-dev-server for changes to take effect
default: &default
source_path: app/javascript
source_entry_path: packs
public_output_path: packs
cache_path: tmp/cache/webpacker
# Additional paths webpack should lookup modules
# ['app/assets', 'engine/foo/app/assets']
resolved_paths: []
# Reload manifest.json on all requests so we reload latest compiled packs
cache_manifest: false
extensions:
- .js
- .sass
- .scss
- .css
- .module.sass
- .module.scss
- .module.css
- .png
- .svg
- .gif
- .jpeg
- .jpg
development:
<<: *default
compile: true
# Reference: https://webpack.js.org/configuration/dev-server/
dev_server:
https: false
host: localhost
port: 3035
public: localhost:3035
hmr: false
# Inline should be set to true if using HMR
inline: true
overlay: true
compress: true
disable_host_check: true
use_local_ip: false
quiet: false
headers:
'Access-Control-Allow-Origin': '*'
watch_options:
ignored: /node_modules/
test:
<<: *default
compile: true
# Compile test packs to a separate directory
public_output_path: packs-test
production:
<<: *default
# Production depends on precompilation of packs prior to booting for performance.
compile: false
# Cache manifest.json for performance
cache_manifest: true
{
"name": "schedules",
"private": true,
"dependencies": {}
"dependencies": {
"@babel/polyfill": "^7.0.0",
"@rails/webpacker": "3.5",
"file-saver": "^2.0.0",
"fullcalendar": "^3.9.0",
"html2canvas": "^1.0.0-alpha.12",
"jquery": "^3.3.1",
"moment": "^2.23.0",
"url-polyfill": "^1.1.3"
},
"devDependencies": {
"webpack-dev-server": "2.11.2"
}
}
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment