Updated to nearly fully functional. Pre-release 1. Still needs some UI changes in the slideshow and admin pages (move the save button & fix the save detection for the internship list). Customer had one more page change request which I need to re-define and handle.

This commit is contained in:
Wynne Crisman
2018-12-12 11:04:00 -08:00
parent c0e971774e
commit 3fc374eda3
108 changed files with 3472 additions and 628 deletions

View File

@@ -1,7 +0,0 @@
<template name="AppreciationEditor">
<div id="appreciationEditor">
<h1>Appreciation Editor</h1>
<div class="editor"></div>
<button id="save">Save</button>
</div>
</template>

View File

@@ -1,6 +0,0 @@
#appreciationEditor
display: block
.ck.ck-editor__editable_inline
border-color: rgba(0,0,0,.2)
.ck.ck-editor__editable_inline.ck-focused
border-color: rgba(0,0,0,1)

View File

@@ -1,87 +0,0 @@
import './AppreciationEditor.html';
import swal from "sweetalert2";
let originalData = "";
Tracker.autorun(function() {
Meteor.subscribe("pages");
});
Template.AppreciationEditor.onRendered(function() {
let _this = this;
//#appreciationEditor'
// CKEditor.create(document.querySelector('#editor'), {}).then(editor => {
// _this.ckEditor = editor;
//
// Tracker.autorun(function() {
// let doc = Meteor.collections.Pages.findOne({name: 'Appreciation'});
//
// originalData = (doc === undefined ? "" : doc.html);
// editor.setData(originalData);
// });
// }).catch(err => {
// console.error(err);
// });
$('.editor').tinymce({
inline: true
});
Tracker.autorun(function() {
let doc = Meteor.collections.Pages.findOne({name: 'Appreciation'});
originalData = (doc === undefined ? "" : doc.html);
$('.editor').html(originalData);
});
});
Template.AppreciationEditor.onDestroyed(function() {
// let data = this.ckEditor.getData();
let data = $('.editor').html();
if(data != originalData) {
swal({
title: "Save Changes",
text: "Would you like to save any changes you have made to this sheet?",
type: "question",
showCancelButton: true,
confirmButtonColor: "#7cdd7f",
confirmButtonText: "Yes",
cancelButtonText: "No"
}).then(
function(isConfirm) {
if(isConfirm) {
Meteor.call('updatePage', 'Appreciation', data, function (error, result) {
if (error) sAlert.error(error);
else sAlert.success("Content Saved Successfully");
});
}
},
function(dismiss) {}
);
}
});
Template.AppreciationEditor.helpers({
// html: function() {
// let doc = Meteor.collections.Pages.findOne({name: 'Appreciation'});
//
// return doc == undefined ? "" : doc.html;
// }
});
Template.AppreciationEditor.events({
'click #save': function(event, template) {
// let data = template.ckEditor.getData();
let data = $('.editor').html();
if(data != originalData) {
Meteor.call('updatePage', 'Appreciation', data, function (error, result) {
if (error) sAlert.error(error);
else sAlert.success("Content Saved Successfully");
});
}
else {
sAlert.success("Data has not changed!");
}
}
});

View File

@@ -1,7 +0,0 @@
<template name="BoardEditor">
<div id="boardEditor">
<h1>Current Board Editor</h1>
<div id="editor"></div>
<button id="save">Save</button>
</div>
</template>

View File

@@ -1,68 +0,0 @@
import './BoardEditor.html';
import swal from "sweetalert2";
let originalData = "";
Tracker.autorun(function() {
Meteor.subscribe("pages");
});
Template.BoardEditor.onRendered(function() {
let _this = this;
CKEditor.create(document.querySelector('#editor'), {}).then(editor => {
_this.ckEditor = editor;
Tracker.autorun(function() {
let doc = Meteor.collections.Pages.findOne({name: 'Board'});
originalData = (doc === undefined ? "" : doc.html);
editor.setData(originalData);
});
}).catch(err => {
console.error(err);
});
});
Template.BoardEditor.onDestroyed(function() {
let data = this.ckEditor.getData();
if(data != originalData) {
swal({
title: "Save Changes",
text: "Would you like to save any changes you have made to this sheet?",
type: "question",
showCancelButton: true,
confirmButtonColor: "#7cdd7f",
confirmButtonText: "Yes",
cancelButtonText: "No"
}).then(
function(isConfirm) {
if(isConfirm) {
Meteor.call('updatePage', 'Board', data, function (error, result) {
if (error) sAlert.error(error);
else sAlert.success("Content Saved Successfully");
});
}
},
function(dismiss) {}
);
}
});
Template.BoardEditor.events({
'click #save': function(event, template) {
let data = template.ckEditor.getData();
if(data != originalData) {
Meteor.call('updatePage', 'Board', data, function (error, result) {
if (error) sAlert.error(error);
else sAlert.success("Content Saved Successfully");
});
}
else {
sAlert.success("Data has not changed!");
}
}
});

View File

@@ -1,7 +0,0 @@
<template name="DatesEditor">
<div id="datesEditor">
<h1>Important Dates Editor</h1>
<div id="editor"></div>
<button id="save">Save</button>
</div>
</template>

View File

@@ -1,6 +0,0 @@
#datesEditor
display: block
.ck.ck-editor__editable_inline
border-color: rgba(0,0,0,.2)
.ck.ck-editor__editable_inline.ck-focused
border-color: rgba(0,0,0,1)

View File

@@ -1,69 +0,0 @@
import './DatesEditor.html';
import swal from "sweetalert2";
let originalData = "";
Tracker.autorun(function() {
Meteor.subscribe("pages");
});
Template.DatesEditor.onRendered(function() {
let _this = this;
CKEditor.create(document.querySelector('#editor'), {}).then(editor => {
_this.ckEditor = editor;
Tracker.autorun(function() {
let doc = Meteor.collections.Pages.findOne({name: 'Dates'});
originalData = (doc === undefined ? "" : doc.html);
editor.setData(originalData);
});
}).catch(err => {
console.error(err);
});
});
Template.DatesEditor.onDestroyed(function() {
let data = this.ckEditor.getData();
if(data != originalData) {
swal({
title: "Save Changes",
text: "Would you like to save any changes you have made to this sheet?",
type: "question",
showCancelButton: true,
confirmButtonColor: "#7cdd7f",
confirmButtonText: "Yes",
cancelButtonText: "No"
}).then(
function(isConfirm) {
if(isConfirm) {
Meteor.call('updatePage', 'Dates', data, function (error, result) {
if (error) sAlert.error(error);
else sAlert.success("Content Saved Successfully");
});
}
},
function(dismiss) {}
);
}
});
Template.DatesEditor.events({
'click #save': function(event, template) {
let data = template.ckEditor.getData();
if(data != originalData) {
Meteor.call('updatePage', 'Dates', data, function (error, result) {
if (error) sAlert.error(error);
else sAlert.success("Content Saved Successfully");
});
}
else {
sAlert.success("Data has not changed!");
}
}
});
s

View File

@@ -0,0 +1,33 @@
<template name="InternshipEditor">
<div id="internshipEditor">
<div class="internshipNavigation">
<div class="addInternship"><input type="text" name="newInternshipName" class="newInternshipName form-control"/><i class="fa fa-plus-circle createInternship noselect clickable" aria-hidden="true"></i></div><span class="editPageText noselect clickable"><i class="fa fa-pencil" aria-hidden="true"></i><br/>Header</span>
<ul class="internshipList">
{{#each internships}}
<li class="internshipListItem noselect clickable" data-id="{{_id}}"><span class="name">{{name}}</span><i class="fa fa-times-circle deleteInternship" aria-hidden="true"></i></li>
{{/each}}
</ul>
</div>
<div class="internshipHtml">
{{> InternshipHtmlEditor internship=selectedInternship}}
</div>
</div>
</template>
<template name="InternshipHtmlEditor">
{{#if showInstructions}}
<h2>Instructions</h2>
<p>Add internships using the +. Type in the internship's name as it will appear in the list to the user, then click the + a second time to add it.
Edit the internships by selecting them in the list. The contents will replace these instructions, and you can edit the internship HTML directly.</p>
{{/if}}
{{#if showSelectImageDialog}}
<div class="modalBackground">
{{> SelectImageDialog (selectImageDialogArgs)}}
</div>
{{/if}}
<div class="editorContainer {{#if showInstructions}}hidden{{/if}}">
<div class="editor textView"></div>
<button id="save">Save</button>
</div>
</template>

View File

@@ -0,0 +1,118 @@
#internshipEditor
display: block
.internshipNavigation
float: left
clear: none
display: inline-block
min-width: 200px
width: 20%
padding-right: 20px
position: relative
padding-top: 50px
.addInternship
margin-bottom: 10px
position: absolute
left: 0
top: 0
right: 60px
height: 40px
input[name="newInternshipName"]
display: inline-block
width: 0
transition: all .75s ease
border: 0
opacity: 0
font-size: 1.2em
input[name="newInternshipName"].show
opacity: 1
border: 1px solid #ccc
border-radius: 4px
width: 70%
transform: translateX(4px)
.createInternship
display: inline-block
position: relative
top: 2px
padding: 2px 6px
margin: 0 4px
//width: 33px
text-align: center
font-size: 1.5em
line-height: 1.5em
border-radius: 8px
//border: 1px solid rgba(0, 0, 0, 0)
box-sizing: border-box
.createInternship:hover
//border: 1px inset #b100d1
//-webkit-box-shadow: inset 0px 0px 20px 0px #de7cff
//-moz-box-shadow: inset 0px 0px 20px 0px #de7cff
//box-shadow: inset 0px 0px 20px 0px #de7cff
color: #5c8744
.createInternship
transform: translateX(-25px) rotate(0deg)
transition: all .75s ease
.createInternship.move
transform: translateX(6px) rotate(720deg)
.editPageText
position: absolute
right: 0
top: 0
width: 60px
font-size: 14px
text-align: center
line-height: 14px
font-weight: 800
border: 1.5px solid gray
border-radius: 6px
padding: 6px
margin-right: 5px
.editPageText:hover
background-color: #c6c6c6
.editPageText:active
border-color: black
.internshipListItem
display: block
background: white
padding: 2px 0
white-space: nowrap
position: relative
height: 24px
.name
position: absolute
left: 0
top: 0
bottom: 0
right: 24px
overflow: hidden
.deleteInternship
position: absolute
right: 0
top: 0
bottom: 0
width: 24px
padding-top: 0px
padding-left: 2px
color: #7c0000
line-height: 24px
font-size: 18px
.deleteInternship:hover
font-size: 22px
top: 0
right: 2px
color: #ab0000
.deleteInternship:active
color: #613434
.internshipListItem:hover
background: #e2ddc0
.internshipListItem:active
background: rgba(254, 255, 0, 0.56)
.internshipListItem.selected
background: rgba(254, 255, 0, 0.56)
.internshipHtml
display: inline-block
max-width: 80%
min-width: 200px
width: 100%

View File

@@ -0,0 +1,228 @@
import './InternshipEditor.html';
import '/imports/ui/dialogs/SelectImageDialog.js';
import swal from "sweetalert2";
const PREFIX = "InternshipEditor_";
Tracker.autorun(function() {
Meteor.subscribe("Internship");
});
Template.InternshipEditor.onCreated(function() {
this.internships = Meteor.collections.Internship.find({}, {sort: {name: 1}});
Session.set(PREFIX + 'selectedInternship', null);
});
Template.InternshipEditor.helpers({
internships: function() {
return Template.instance().internships;
},
selectedInternship: function() {
return Session.get(PREFIX + "selectedInternship");
}
});
Template.InternshipEditor.events({
'click .deleteInternship': function(event, template) {
let $li = template.$(event.target).parent();
Meteor.call('removeInternship', $li.data('id'), function(error, id) {
if(error) sAlert.error("Failed to create the internship!\n" + error);
});
},
'click .internshipListItem': function(event, template) {
let $li = template.$(event.currentTarget);
$li.addClass('selected');
$li.siblings().removeClass('selected');
Session.set(PREFIX + "selectedInternship", Meteor.collections.Internship.findOne($li.data('id')));
},
'click .editPageText': function(event,template) {
},
"keyup input[name='newInternshipName']" : function(event, template) {
if(event.keyCode === 13) {
event.preventDefault();
$('.createInternship').trigger('click');
}
},
"click .createInternship": function(event, template) {
let $input = template.$('input[name="newInternshipName"]');
if($input.hasClass('show')) {
let name = $input.val();
name = name ? name.trim() : undefined;
name = name && name.length > 0 ? name : undefined;
if(name) {
let content = "<p><strong>Job Title:&nbsp;</strong></p>\n<p><strong>Supervisor / Sponsor:&nbsp;</strong></p>\n<p><strong>Location of Internship:&nbsp;</strong></p>\n<p><strong>Dates &amp; hours:&nbsp;</strong></p>\n<p><strong>Duties &amp; Activities:&nbsp;</strong></p>\n<p><strong>Desirable Qualities / Skills:&nbsp;</strong></p>";
//Meteor.collections.Internship.insert({name, content});
Meteor.call('addInternship', name, content, function(error, id) {
if(error) sAlert.error("Failed to create the internship!\n" + error);
else {
//Clear the text editor.
$input.val("");
//Quick hack to attempt to allow the internship we created to be added to the list before we try to select it and edit it.
let count = 0;
let interval = setInterval(function() {
let selected = Meteor.collections.Internship.findOne(id);
if(selected) {
//Select the sheet in the drop down.
let $li = template.$('ul.internshipList li[data-id="' + id + '"]');
$li.addClass("selected");
$li.siblings().removeClass("selected");
Session.set(PREFIX + "selectedInternship", selected);
clearInterval(interval);
}
else count++;
//Avoid infinite loop that should never happen.
if(count > 100) clearInterval(interval);
}, 100);
}
});
}
$input.removeClass('show');
$(event.target).toggleClass('move');
}
else {
$input.addClass('show');
$(event.target).toggleClass('move');
$input.focus();
}
}
});
Template.InternshipHtmlEditor.onCreated(function() {
let template = this;
this.showSelectImageDialog = new ReactiveVar(false);
this.saveChanges = function(ask) {
let data = this.$('.editor').val();
let template = this;
//Only ask the user if they want to update their changes if they actually have changes that have not yet been saved.
if(data !== template.currentHtml) {
const changedData = data;
//Ensure this does not get run twice.
template.currentHtml = changedData;
if(ask) {
//Ask the user if they want to update the repository with their changes.
swal({
title: "Save Changes",
text: "Would you like to save any changes you have made to this page?",
type: "question",
showCancelButton: true,
confirmButtonColor: "#7cdd7f",
confirmButtonText: "Yes",
cancelButtonText: "No"
}).then(
function(isConfirm) {
if(isConfirm) {
Meteor.call('updateInternship', template.data.internship._id, changedData, function (error, result) {
if (error) sAlert.error(error);
else sAlert.success("Content Saved Successfully");
});
}
},
function(dismiss) {}
);
}
else {
Meteor.call('updateInternship', template.data.internship._id, changedData, function (error, result) {
if (error) sAlert.error(error);
else sAlert.success("Content Saved Successfully");
});
}
}
};
});
Template.InternshipHtmlEditor.onRendered(function() {
let template = this;
$('.editor').tinymce({
inline: true,
menubar: false,
theme: 'inlite',
plugins: [
'autolink',
'contextmenu',
'link',
'lists',
'table',
'textcolor'
],
toolbar: [
'undo redo | bold italic underline | fontselect fontsizeselect',
'forecolor backcolor | alignleft aligncenter alignright alignfull | link unlink | numlist bullist outdent indent'
],
table_default_attributes: {
border: 0,
cellpadding: 4
},
table_default_styles: {
borderCollapse: "collapse"
},
insert_toolbar: 'quicktable', //image
selection_toolbar: 'bold italic | h1 h2 h3 | bullist numlist outdent indent | blockquote quicklink',
contextmenu: 'InsertImage | inserttable | cell row column deletetable', //image
contextmenu_never_use_native: false,
setup: function(editor) {
editor.addMenuItem('InsertImage', {
text: "Insert Image",
context: 'tools',
onclick: function() {
template.showSelectImageDialog.set(true);
}
});
}
});
Tracker.autorun(function() {
//let data = Template.currentData().data; //Note: Calling Template.currentData() is a reactive way to get the template parameters.
let data = Blaze.getData(template.view).internship;
if(data) {
template.currentHtml = data.content;
$('.editor').html(data.content && data.content.length > 0 ? data.content : "Change Me!");
}
});
});
Template.InternshipHtmlEditor.onDestroyed(function() {
this.saveChanges(true);
});
Template.InternshipHtmlEditor.events({
'click #save': function(event, template) {
template.saveChanges(false);
}
});
Template.InternshipHtmlEditor.helpers({
showSelectImageDialog() {
return Template.instance().showSelectImageDialog.get();
},
selectImageDialogArgs() {
let template = Template.instance();
return {
onApply(value) {
tinymce.activeEditor.insertContent('<img src="' + value + '"/>');
template.showSelectImageDialog.set(false);
},
onClose() {
template.showSelectImageDialog.set(false);
}
};
},
showInstructions() {
return !Template.currentData().internship;
}
});

View File

@@ -1,4 +0,0 @@
<template name="InternshipsEditor">
<div id="internshipsEditor">
</div>
</template>

View File

@@ -1,2 +0,0 @@
#internshipsEditor
display: block

View File

@@ -1,2 +0,0 @@
import './InternshipsEditor.html';

View File

@@ -1,7 +0,0 @@
<template name="NewsEditor">
<div id="newsEditor">
<h1>News Editor</h1>
<div class="editor"></div>
<button id="save">Save</button>
</div>
</template>

View File

@@ -1,6 +0,0 @@
#newsEditor
display: block
.ck.ck-editor__editable_inline
border-color: rgba(0,0,0,.2)
.ck.ck-editor__editable_inline.ck-focused
border-color: rgba(0,0,0,1)

View File

@@ -1,86 +0,0 @@
import './NewsEditor.html';
import swal from "sweetalert2";
let originalData = "";
Tracker.autorun(function() {
Meteor.subscribe("pages");
});
Template.NewsEditor.onRendered(function() {
let _this = this;
//#appreciationEditor'
// CKEditor.create(document.querySelector('#editor'), {}).then(editor => {
// _this.ckEditor = editor;
//
// Tracker.autorun(function() {
// let doc = Meteor.collections.Pages.findOne({name: 'Appreciation'});
//
// originalData = (doc === undefined ? "" : doc.html);
// editor.setData(originalData);
// });
// }).catch(err => {
// console.error(err);
// });
$('.editor').tinymce({
inline: true
});
Tracker.autorun(function() {
let doc = Meteor.collections.Pages.findOne({name: 'News'});
originalData = (doc === undefined ? "" : doc.html);
$('.editor').html(originalData);
});
});
Template.NewsEditor.onDestroyed(function() {
let data = this.ckEditor.getData();
if(data != originalData) {
swal({
title: "Save Changes",
text: "Would you like to save any changes you have made to this sheet?",
type: "question",
showCancelButton: true,
confirmButtonColor: "#7cdd7f",
confirmButtonText: "Yes",
cancelButtonText: "No"
}).then(
function(isConfirm) {
if(isConfirm) {
Meteor.call('updatePage', 'News', data, function (error, result) {
if (error) sAlert.error(error);
else sAlert.success("Content Saved Successfully");
});
}
},
function(dismiss) {}
);
}
});
Template.NewsEditor.helpers({
// html: function() {
// let doc = Meteor.collections.Pages.findOne({name: 'News'});
//
// return doc == undefined ? "" : doc.html;
// }
});
Template.NewsEditor.events({
'click #save': function(event, template) {
let data = template.ckEditor.getData();
if(data != originalData) {
Meteor.call('updatePage', 'News', data, function (error, result) {
if (error) sAlert.error(error);
else sAlert.success("Content Saved Successfully");
});
}
else {
sAlert.success("Data has not changed!");
}
}
});

View File

@@ -0,0 +1,13 @@
<template name="PageEditor">
<div id="pageEditor">
{{#if showSelectImageDialog}}
<div class="modalBackground">
{{> SelectImageDialog (selectImageDialogArgs)}}
</div>
{{/if}}
<h1>{{editorName}} Editor</h1>
<div class="editor"></div>
<button id="save">Save</button>
</div>
</template>

View File

@@ -0,0 +1,226 @@
import './PageEditor.html';
import '/imports/ui/dialogs/SelectImageDialog.js';
import swal from "sweetalert2";
let currentHtml = "";
let currentPath = "";
let routeData = {
AppreciationEditor: {title: "Appreciation", name: "Appreciation"},
NewsEditor: {title: "News", name: "News"},
DatesEditor: {title: "Dates", name: "Dates"},
BoardEditor: {title: "Current Board", name: "Board"},
SlideshowPageEditor: {title: "Slideshow Page", name: "Slideshow"}
};
Tracker.autorun(function() {
//let name = routeData[FlowRouter.getRouteName()] ? routeData[FlowRouter.getRouteName()].name : "";
//if(name) {
//TODO: Filter the page by the page name.
Meteor.subscribe("pages");
//}
});
Template.PageEditor.onCreated(function() {
let template = this;
this.showSelectImageDialog = new ReactiveVar(false);
this.pageName = new ReactiveVar();
this.saveChanges = function() {
if(currentPath && currentPath !== FlowRouter.getRouteName()) {
let data = this.$('.editor').val();
let template = this;
currentPath = FlowRouter.getRouteName();
//Only ask the user if they want to update their changes if they actually have changes that have not yet been saved.
if(data !== currentHtml) {
const changedData = data;
//Ensure this does not get run twice.
currentHtml = data;
//Ask the user if they want to update the repository with their changes.
swal({
title: "Save Changes",
text: "Would you like to save any changes you have made to this page?",
type: "question",
showCancelButton: true,
confirmButtonColor: "#7cdd7f",
confirmButtonText: "Yes",
cancelButtonText: "No"
}).then(
function(isConfirm) {
if(isConfirm) {
Meteor.call('updatePage', template.pageName.get(), changedData, function (error, result) {
if (error) sAlert.error(error);
else sAlert.success("Content Saved Successfully");
});
}
},
function(dismiss) {}
);
}
}
};
Tracker.autorun(function() {
if(routeData[FlowRouter.getRouteName()]) {
template.saveChanges();
//Save the page's name (indexes the page HTML in the collection) to the reactive variable so that all the content changes automatically.
template.pageName.set(routeData[FlowRouter.getRouteName()].name);
}
});
});
Template.PageEditor.onRendered(function() {
let template = this;
$('.editor').tinymce({
// inline: true,
// menubar: false,
// theme: 'inlite', //inlite
// //skin: 'light',
// plugins: [
// 'autolink',
// 'contextmenu',
// 'link',
//// 'linkchecker', Broken
// 'lists',
//// 'powerpaste', Broken
// 'table',
// //'image',
// 'textcolor'
// ],
// toolbar: [
// 'undo redo | bold italic underline | fontselect fontsizeselect',
// 'forecolor backcolor | alignleft aligncenter alignright alignfull | link unlink | numlist bullist outdent indent | InsertImage | inserttable | cell row column deletetable'
// ],
// table_default_attributes: {
// border: 0,
// cellpadding: 4
// },
// table_default_styles: {
// borderCollapse: "collapse"
// },
// insert_toolbar: 'quicktable', //image
// selection_toolbar: 'bold italic | h1 h2 h3 | blockquote quicklink',
// contextmenu: 'InsertImage | inserttable | cell row column deletetable', //image
// contextmenu_never_use_native: false,
//image_advtab: true,
//image_description: true,
//image_dimensions: true,
//image_title: true,
//image_list: function(success) {
// //Expects an array of objects containing title:String and value:String properties where the value is a path to the image.
// Meteor.call('getGeneralImages', function(data, err) {
// if(err) {
// sAlert.error(err);
// }
// else {
// success(data);
// }
// });
//}
//powerpaste_word_import: 'clean',
//powerpaste_html_import: 'clean'
inline: true,
menubar: false,
theme: 'inlite',
plugins: [
'autolink',
'contextmenu',
'link',
'lists',
'table',
'textcolor'
],
toolbar: [
'undo redo | bold italic underline | fontselect fontsizeselect',
'forecolor backcolor | alignleft aligncenter alignright alignfull | link unlink | numlist bullist outdent indent'
],
table_default_attributes: {
border: 0,
cellpadding: 4
},
table_default_styles: {
borderCollapse: "collapse"
},
insert_toolbar: 'quicktable', //image
selection_toolbar: 'bold italic | h1 h2 h3 | blockquote quicklink',
contextmenu: 'InsertImage | inserttable | cell row column deletetable', //image
contextmenu_never_use_native: false,
setup: function(editor) {
editor.addMenuItem('InsertImage', {
text: "Insert Image",
context: 'tools',
onclick: function() {
template.showSelectImageDialog.set(true);
}
});
}
});
Tracker.autorun(function() {
let doc = Meteor.collections.Pages.findOne({name: template.pageName.get()});
currentHtml = (doc === undefined ? "" : doc.html);
$('.editor').html(currentHtml);
});
});
Template.PageEditor.onDestroyed(function() {
this.saveChanges();
});
Template.PageEditor.events({
'click #save': function(event, template) {
let data = template.$('.editor').val();
if(data !== currentHtml) {
Meteor.call('updatePage', template.pageName.get(), data, function (error, result) {
if (error) sAlert.error(error);
else {
sAlert.success("Content Saved Successfully");
//Ensure we recognize things were saved later.
currentHtml = data;
}
});
}
else {
sAlert.success("Data has not changed!");
}
}
});
Template.PageEditor.helpers({
editorName() {
return routeData[FlowRouter.getRouteName()].title; //FlowRouter.getRouteName() is reactive allowing this to trigger when the content changes.
},
showSelectImageDialog() {
return Template.instance().showSelectImageDialog.get();
},
selectImageDialogArgs() {
let template = Template.instance();
return {
onApply(value) {
tinymce.activeEditor.insertContent('<img src="' + value + '"/>');
template.showSelectImageDialog.set(false);
},
onClose() {
template.showSelectImageDialog.set(false);
}
};
}
});

View File

@@ -0,0 +1,41 @@
<template name="SlideshowEditor">
<div id="slideshowEditor">
<h1>Slideshow Editor</h1>
<div class="slideshowsGroup" style="vertical-align: bottom">
<label class='controlLabel'>Selected Slideshow: </label>
<select name="slideshows">
{{#each slideshows}}
<option value="{{_id}}">{{name}}</option>
{{/each}}
</select>
<input type="text" name="newSlideshowName" class="newSlideshowName form-control"/><i class="fa fa-plus-circle createSlideshow noselect clickable" aria-hidden="true"></i>{{#if selectedSlideshow}}<i class="fa fa-trash deleteSlideshow noselect clickable" aria-hidden="true"></i>{{/if}}
</div>
{{#if selectedSlideshow}}
{{> SlideshowContentEditor selectedSlideshow._id}}
{{/if}}
</div>
</template>
<template name="SlideshowContentEditor">
<div id="slideshowContentEditor">
{{#if showSelectImageDialog}}
<div class="modalBackground">
{{> SelectImageDialog (selectImageDialogArgs)}}
</div>
{{/if}}
{{#each slides}}
<div class="slideThumbnail" style="background-image: url('/slideshow-image/{{this}}')" data-image-id="{{this}}">
<div class="controls">
<i class="fa fa-angle-double-left moveLeft noselect clickable" aria-hidden="true">
</i><i class="fa fa-times removeThumbnail noselect clickable" aria-hidden="true">
</i><i class="fa fa-angle-double-right moveRight noselect clickable" aria-hidden="true"></i>
</div>
</div>
<!--<p>{{this}}</p>-->
{{/each}}
<div class="addSlide noselect clickable" style='background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMzAwcHgiIHdpZHRoPSIzMDBweCIgZmlsbD0iIzAwMDAwMCIgdmVyc2lvbj0iMS4xIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDEzOSAxMzkiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDEzOSAxMzkiIHhtbDpzcGFjZT0icHJlc2VydmUiIGlkPSJzdmcxMCI+IDxkZWZzIGlkPSJkZWZzMTQiIC8+PHBhdGggZD0iTTY5LjQ5OSw4LjYxOWMtMzMuNTQzLDAtNjAuODMzLDI3LjI5LTYwLjgzMyw2MC44MzRjMCwzMy41NDUsMjcuMjksNjAuODM1LDYwLjgzMyw2MC44MzUgIGMzMy41NDUsMCw2MC44MzYtMjcuMjksNjAuODM2LTYwLjgzNUMxMzAuMzM1LDM1LjkwOSwxMDMuMDQ0LDguNjE5LDY5LjQ5OSw4LjYxOXogTTY5LjQ5OSwxMjQuMjg4ICBjLTMwLjIzNSwwLTU0LjgzMy0yNC41OTktNTQuODMzLTU0LjgzNWMwLTMwLjIzNSwyNC41OTgtNTQuODM0LDU0LjgzMy01NC44MzRjMzAuMjM2LDAsNTQuODM2LDI0LjU5OSw1NC44MzYsNTQuODM0ICBDMTI0LjMzNSw5OS42ODksOTkuNzM1LDEyNC4yODgsNjkuNDk5LDEyNC4yODh6IiBpZD0icGF0aDIiIC8+PHBhdGggZD0iTSA4NS42NCw5NS42OCA4NC4xMTk3MTgsOTIuNTM1ODQ4IDQyLjYzNiw5Mi42Mzk5NjYgNDIuNjM2LDQ2LjI1NiBIIDk2LjMwMyBWIDcyLjMyIGggMi4zNyA0LjE2MiBWIDQyLjYgYyAwLC0xLjUzNCAtMS4yNDMsLTIuNzc3IC0yLjc3OCwtMi43NzcgSCAzOC45NDUgYyAtMS41MzUsMCAtMi43NzgsMS4yNDMgLTIuNzc4LDIuNzc3IHYgNTMuNzA1IGMgMCwxLjUzNCAxLjI0MywyLjc3NyAyLjc3OCwyLjc3NyBIIDk0LjY3MyBWIDk1LjY4IEggODkuNjQgWiIgaWQ9InBhdGg0Ii8+PHBvbHlnb24gcG9pbnRzPSIxMDQuOTk5LDc2LjMyIDEwMi44MzUsNzYuMzIgOTguNjczLDc2LjMyIDk4LjY3Myw4NS4zNTMgODkuNjQsODUuMzUzIDg5LjY0LDkxLjY4IDk4LjY3Myw5MS42OCA5OC42NzMsOTkuMDgzIDk4LjY3MywxMDAuNzEyIDEwNC45OTksMTAwLjcxMiAxMDQuOTk5LDkxLjY4IDExNC4wMzEsOTEuNjggMTE0LjAzMSw4NS4zNTMgMTA0Ljk5OSw4NS4zNTMgIiBpZD0icG9seWdvbjYiIHRyYW5zZm9ybT0ibWF0cml4KDEuMDM0MTQ4NiwwLDAsMS4wMzQxNDg2LC01LjcwMDk4OCwtMS45NzI4ODk2KSIgLz48Y2lyY2xlIGN4PSI2Mi42MTM1NzkiIGN5PSI2NC42NDAyMjgiIHI9IjUuNzQ4MDAwMSIgaWQ9ImNpcmNsZTgiIHN0eWxlPSJmaWxsOiNmZWM5NDE7ZmlsbC1vcGFjaXR5OjEiIC8+PHBhdGggc3R5bGU9ImZpbGw6IzU0NDgwMDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MC40NjMzMzMzNHB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiIGQ9Im0gNDcuMDYxNTQ5LDg3LjE0ODk5OSBjIDAsMCA1LjU2NTY4OSwtMjQuMzkzMzg5IDcuODA4ODg2LC0yNC41NzE5NjQgMi4yNDMxOTcsLTAuMTc4NTc1IDcuMzkyNDE0LDkuODkxMjU5IDcuMzkyNDE0LDkuODkxMjU5IDAsMCAyLjI5MTI1MiwtMy44OTYwMTYgMy41NDAwMjksLTMuNTQwMDI5IDEuMjQ4Nzc3LDAuMzU1OTg3IDYuOTcwMTMzLDcuOTQ5ODkgOC4xMjEyNDEsOC42NDE4MzQgMS4xNTExMDgsMC42OTE5NDQgMy41NzYxMjksLTIuNzc5NjQ0IDUuNzI2NTE0LC0zLjEyMzU1NiAyLjE1MDM4NSwtMC4zNDM5MTIgNy40OTY1MzMsOC4wMTcxMjUgNy40OTY1MzMsOC4wMTcxMjUgaCAtMS44NzcyIGwgLTEuNzY2OTQ5LDQuNzg5NDQ2IHoiIGlkPSJwYXRoODIzIi8+PC9zdmc+");'></div>
</div>
</template>

View File

@@ -0,0 +1,108 @@
#slideshowEditor
display: block
select[name='slideshows']
font-size: 1.5em
min-width: 100px
input[name="newSlideshowName"]
display: inline-block
transition: all .75s ease
width: 0
border: 0
opacity: 0
font-size: 1.2em
input[name="newSlideshowName"].show
opacity: 1
border: 1px solid #ccc
border-radius: 4px
width: 200px
transform: translateX(4px)
.createSlideshow
display: inline-block
padding: 2px 6px
margin: 0 4px
//width: 33px
text-align: center
font-size: 1.5em
line-height: 1.5em
border-radius: 8px
//border: 1px solid rgba(0, 0, 0, 0)
box-sizing: border-box
.createSlideshow:hover
//border: 1px inset #b100d1
//-webkit-box-shadow: inset 0px 0px 20px 0px #de7cff
//-moz-box-shadow: inset 0px 0px 20px 0px #de7cff
//box-shadow: inset 0px 0px 20px 0px #de7cff
color: #5c8744
.createSlideshow
transform: translateX(-25px) rotate(0deg)
transition: all .75s ease
.createSlideshow.move
transform: translateX(6px) rotate(720deg)
.deleteSlideshow
display: inline-block
padding: 2px 3px
margin: 0 4px
width: 1.5em
line-height: 1.5em
//width: 33px
text-align: center
font-size: 1.5em
border-radius: 50%
//border: 1px solid #8c0000
box-sizing: border-box
color: black
.deleteSlideshow:hover
color: #8c2353
.addSlide
display:inline-block
width: 200px
height: 200px
background-size: contain
background-repeat: no-repeat
border-radius: 6px
border 3px dashed black
.addSlide:hover
border-color: #5c8744
.slideThumbnail
position: relative
width: 200px
height: 200px
background-position: 50% 50%
background-repeat:no-repeat
background-size: contain
display: inline-block
.controls
position: absolute
bottom: 50%
left: 50%
right: 50%
top: 50%
height: 22px
width: 90px
margin-left: -45px
margin-top: -10px
background-color: rgba(0, 0, 0, .8)
display: none
text-align: center
font-size: 20px
line-height: 20px
border-radius: 8px
border: 1px solid black
.slideThumbnail:hover > .controls, .controls:hover
display: block
.moveLeft, .moveRight, .removeThumbnail
color: white
padding: 0 4px
.moveLeft
padding-right: 14px
.moveRight
padding-left: 14px
.moveLeft:hover, .moveRight:hover
color: #00ff55
.moveLeft:active, .moveRight:active
color: #006e27
.removeThumbnail:hover
color: #ff0019
.removeThumbnail:active
color: #57000c

View File

@@ -0,0 +1,213 @@
import './SlideshowEditor.html';
import '/imports/ui/dialogs/SelectImageDialog.js';
import swal from "sweetalert2";
const PREFIX = "SlideshowEditor_";
Tracker.autorun(function() {
Meteor.subscribe("slideshow");
});
Template.SlideshowEditor.onCreated(function() {
this.slideshows = Meteor.collections.Slideshow.find({}, {sort: {name: 1}});
Session.set(PREFIX + 'selectedSlideshow', null);
});
Template.SlideshowEditor.onRendered(function() {
let _this = this;
Tracker.autorun(function() {
//This is a reactive call, allowing this function to be re-run when the cursor changes.
let slideshows = _this.slideshows.fetch();
if(slideshows.length > 0 && !Session.get(PREFIX + "selectedSlideshow")) {
//Mark the first slideshow as selected because it will be selected in the UI by default.
Session.set(PREFIX + "selectedSlideshow", slideshows[0]);
}
});
});
Template.SlideshowEditor.onDestroyed(function() {
});
Template.SlideshowEditor.helpers({
slideshows: function() {
return Template.instance().slideshows;
},
selectedSlideshow: function(){
return Session.get(PREFIX + "selectedSlideshow");
},
isSlideshowSelected: function() {
let selectedSlideshow = Session.get(PREFIX + "selectedSlideshow");
return selectedSlideshow == this ? "selected" : "";
},
hasSelectedSlideshow: function() {
return Session.get(PREFIX + "selectedSlideshow");
}
});
Template.SlideshowEditor.events({
'change select[name="slideshows"]': function(event, template) {
let slideshowId = $(event.target).val();
let slideshow = Meteor.collections.Slideshow.findOne(slideshowId);
Session.set(PREFIX + 'selectedSlideshow', slideshow);
},
"keyup input[name='newSlideshowName']" : function(event, template) {
if(event.keyCode === 13) {
event.preventDefault();
$('.createSlideshow').trigger('click');
}
},
"click .createSlideshow": function(event, template) {
let $input = template.$('input[name="newSlideshowName"]');
if($input.hasClass('show')) {
let name = $input.val();
name = name ? name.trim() : undefined;
name = name && name.length > 0 ? name : undefined;
if(name) {
Meteor.call('addSlideshow', name, function(error, id) {
if(error) sAlert.error("Failed to create the slideshow!\n" + error);
else {
//Clear the text editor.
$input.val("");
//Quick hack to attempt to allow the slideshow we created to be added to the select box before we try to select it and edit it.
let count = 0;
let interval = setInterval(function() {
let selected = Meteor.collections.Slideshow.findOne(id);
if(selected) {
//Select the sheet in the drop down.
template.$('select[name="slideshows"]').val(id);
Session.set(PREFIX + "selectedSlideshow", selected);
clearInterval(interval);
}
else count++;
//Avoid infinite loop that should never happen.
if(count > 100) clearInterval(interval);
}, 100);
}
});
}
$input.removeClass('show');
$(event.target).toggleClass('move');
template.$('.deleteSlideshow').show();
}
else {
$input.addClass('show');
$(event.target).toggleClass('move');
$input.focus();
template.$('.deleteSlideshow').hide();
}
},
"click .deleteSlideshow": function(event, template) {
let $combo = template.$('select[name="slideshows"]');
let slideshow = Session.get(PREFIX + 'selectedSlideshow');
if(slideshow) {
swal({
title: 'Are you sure?',
text: "You won't be able to revert this!",
type: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: 'Yes, delete it!'
}).then((result) => {
Meteor.call('removeSlideshow', slideshow._id, function(error, id) {
if(error) sAlert.error("Failed to remove the slideshow!\n" + error);
else {
sAlert.success("Slideshow removed.");
//Fire the event so that our selection changes once the slideshow is removed from the combo.
$combo.trigger('change');
}
});
});
}
}
});
Template.SlideshowContentEditor.onCreated(function() {
this.showSelectImageDialog = new ReactiveVar(false);
});
Template.SlideshowContentEditor.helpers({
showSelectImageDialog: function() {
return Template.instance().showSelectImageDialog.get();
},
slides: function() {
return Meteor.collections.Slideshow.findOne(Template.instance().data).images;
},
selectImageDialogArgs() {
let template = Template.instance();
return {
onApply(data) {
//tinymce.activeEditor.insertContent('<img src="' + value + '"/>');
template.showSelectImageDialog.set(false);
//Send the slide image as a base64 string.
Meteor.call('addSlideshowImage', template.data, data, function(error, id) {
if(error) sAlert.error("Failed to add the slide.\n" + error);
else {
//TODO: ?
}
});
},
onClose() {
template.showSelectImageDialog.set(false);
}
};
}
//getImage(id) {
// Meteor.call('getSlideshowImage', id, function(error, base64)) {
// if(error) sAlert.error("Failed to get the slide image.\n" + error);
// else {
//
// }
// }
//}
});
Template.SlideshowContentEditor.events({
"click .addSlide": function(event, template) {
template.showSelectImageDialog.set(true);
},
'click .moveLeft': function(event, template) {
let $slideThumbnail = $(event.target).parent().parent();
let index = $slideThumbnail.index();
//If we can move it to the right then do so.
if(index - 1 >= 0) {
Meteor.call('swapSlideshowImages', template.data, index, index - 1, function(error) {
if(error) sAlert.error("Failed to move the slide.\n" + error);
});
}
},
'click .moveRight': function(event, template) {
let $slideThumbnail = $(event.target).parent().parent();
let index = $slideThumbnail.index();
let thumbnailCount = $slideThumbnail.siblings().length; //Should give us the number of siblings including the add image button (we don't want to count it), and excluding this slide thumbnail (we do want to count it).
//If we can move it to the right then do so.
if(index + 1 < thumbnailCount) {
Meteor.call('swapSlideshowImages', template.data, index, index + 1, function(error) {
if(error) sAlert.error("Failed to move the slide.\n" + error);
});
}
},
'click .removeThumbnail': function(event, template) {
let $slideThumbnail = $(event.target).parent().parent();
let imageId = $slideThumbnail.data("image-id");
Meteor.call('removeSlideshowImage', template.data, imageId, function(error) {
if(error) sAlert.error("Failed to remove the slide.\n" + error);
});
}
});

View File

@@ -0,0 +1,7 @@
<template name="EditablePage">
<div id="editablePage" class="textView">
{{{editableHTML}}}
<!-- This prevents the text from being smaller than the image and the footer from being shoved next to the image. -->
<div style="clear:both"></div>
</div>
</template>

32
imports/ui/EditablePage.import.styl vendored Normal file
View File

@@ -0,0 +1,32 @@
#pageEditor div.editor
max-width: 960px
padding: 20px
#editablePage, #pageEditor div.editor, .editablePage
p
font-family: Palatino, "Palatino Linotype", "Palatino LT STD", "Book Antiqua", Georgia, serif
font-size: 16px
margin: 16px 0
line-height: 20.8px
h2
font-family: "Arial Black", "Arial Bold", Gadget, sans-serif
font-size: 19px
text-transform: uppercase
background-color: #EEE
font-weight: bold
padding-left: 40px
line-height: 24px
display: inline-block
h4
font-family: "Century Gothic", CenturyGothic, AppleGothic, sans-serif
font-size: 16px
font-weight: 800
clear: left
display: inline-block
line-height: 20.8px
img
padding-right: 10px
ul
margin-top: 10px
font-family: Palatino, "Palatino Linotype", "Palatino LT STD", "Book Antiqua", Georgia, serif
font-size: 16px
line-height: 20.8px

View File

@@ -0,0 +1,33 @@
import './EditablePage.html';
let routeData = {
Appreciation: {name: "Appreciation"},
News: {name: "News"},
ImportantDates: {name: "Dates"},
CurrentBoard: {name: "Board"}
};
Tracker.autorun(function() {
Meteor.subscribe("pages");
});
Template.EditablePage.onCreated(function() {
let template = this;
this.pageName = new ReactiveVar();
Tracker.autorun(function() {
if(routeData[FlowRouter.getRouteName()]) {
//Save the page's name (indexes the page HTML in the collection) to the reactive variable so that all the content changes automatically.
template.pageName.set(routeData[FlowRouter.getRouteName()].name);
}
});
});
Template.EditablePage.helpers({
editableHTML: function() {
let doc = Meteor.collections.Pages.findOne({name: Template.instance().pageName.get()});
return doc === undefined ? "" : doc.html;
}
});

View File

@@ -1,7 +1,7 @@
<template name="Fellowships">
<div id="fellowshipsView" class="textView">
<div id="fellowshipDescription">
<img class="pageHeaderPicture" src="./images/fir_v5.jpg">
<img class="pageHeaderPicture" src="./images/fellowships.jpg">
<h3 class="fellowshipProgram">Fellowship Program</h3>
<p class="generalText">High School Juniors looking for an exciting and challenging summer experience may be motivated to apply for an AVEF Summer Fellowship. AVEF will support fees, transportation, living expenses and incidentals for a program selected by the student. Students should begin by analyzing their interests, aptitudes and passions to guide them in finding a summer program. The program of interest can be located within the United States or even abroad. The program of interest must have an educational component.</p>
<p class="generalText">Students must complete the AVEF application before the deadline and interview with our selection committee. This program is competitive and open to current juniors only. The Fellowship Application Form is available below.</p>
@@ -26,7 +26,7 @@
<h3 class="relevantLinks">Relevant Links</h3>
<div>
<ul style="list-style-type: none;">
<li class="generalLinks"><a href="#!/dates">Important Dates</a></li>
<li class="generalLinks"><a href="/ImportantDates">Important Dates</a></li>
<li class="generalLinks"><a href="/forms/Fellowship_Application_Form.pdf" download="Fellowship Application Form.pdf">Fellowship Application Form</a> (307KB PDF Savable/Printable Form)</li>
</ul>
</div>

View File

@@ -1,7 +1,7 @@
<template name="Grants">
<div id="grantView" class="textView">
<div class="grantDescription">
<img class="pageHeaderPicture" src="./images/apple_v5.jpg"/>
<img class="pageHeaderPicture" src="./images/grants.jpg"/>
<div class="grantProgram">Grant Program</div>
<p>Every year AVEF provides financial support to teachers, students, parents, mentors or community groups for a wide variety of projects. The purpose of this program is to enhance the educational opportunities available to the youth of Anderson Valley. All applications for grants must be received in writing. Guidelines for grant requests are below. The AVEF board reviews and responds to these requests at its monthly meeting.</p>
<p>Any community member - teacher, student, parent, mentor or community group - may submit a proposal for an AVEF grant. Grant applications are accepted throughout the year. Grant applications can be mailed to AVEF Box 242, Boonville, CA 95415 or can be given to any board member to present at our meeting.</p>

View File

@@ -21,7 +21,7 @@
<div class="menuHeader">Fellowships</div>
<!--<a style="display: none" href="#!/fellowship">fellowship</a>-->
</a>
<a class="menuTile newsMenu" href="{{pathFor 'News&Notices'}}">
<a class="menuTile newsMenu" href="{{pathFor 'News'}}">
<div class="menuHeaderBackground"></div>
<div class="menuHeader">News &amp; Notices</div>
<div id="newsHeaderTimestampDiv" class="menuHeaderTimestamp">03/13/18</div>

View File

@@ -1,5 +1,16 @@
<template name="InternshipJobs">
<div id="internshipJobs">
Hello World
<div id="internshipJobs" class="textView">
<h3>Internship Job List</h3>
<ol>
{{#each internships}}
<li><a href="/Internship-Job/{{_id}}">{{name}}</a></li>
{{/each}}
</ol>
</div>
</template>
<template name="InternshipJob">
<div id="internshipJob" class="textView">
{{{internship.content}}}
</div>
</template>

View File

@@ -1,2 +1,2 @@
#internshipJobs
margin: 20px 40px
display: block

View File

@@ -1,2 +1,29 @@
import './InternshipJobs.html';
import './InternshipJobs.html';
Tracker.autorun(function() {
Meteor.subscribe("Internship");
});
Template.InternshipJobs.onCreated(function() {
this.internships = Meteor.collections.Internship.find({}, {sort: {name: 1}});
});
Template.InternshipJobs.helpers({
internships: function() {
return Template.instance().internships;
}
});
Template.InternshipJobs.events({
});
Template.InternshipJob.onCreated(function() {
});
Template.InternshipJob.helpers({
internship: function() {
let id = FlowRouter.getParam('_id');
return Meteor.collections.Internship.findOne(id);
}
});
Template.InternshipJob.events({
});

View File

@@ -1,7 +1,7 @@
<template name="Internships">
<div id="internshipsView">
<div id="internshipsView" class="textView">
<div id="internshipDescription">
<img class="pageHeaderPicture" src="./images/oak_v5.jpg">
<img class="pageHeaderPicture" src="./images/internships.jpg">
<h3 class="internshipProgram">Internship Program</h3>
<p class="internshipDescription">This is a chance for high school students to try something new and gain job experience. Every year AVEF supports a number of summer work opportunities for current high school students. Local businesses and individuals who agree to mentor a student supply job descriptions for their specific needs. These job descriptions will be available for students to review in March of each year. Students are selected by the mentor on the basis of the student's application and interview. The Internship Job List and Internship Student Application Form are available below. Students who successfully complete the required 80 hours of work are paid.</p>
</div>
@@ -88,10 +88,10 @@
<h3 class="relevantLinks">Relevant Links</h3>
<div>
<ul class="relevantLinksContent">
<li><a href="#!/dates">Important Dates</a></li>
<li><a href="#!/internship-job-list">Internship Job List</a></li>
<li><a href="ImportantDates">Important Dates</a></li>
<li><a href="Internship-Job-List">Internship Job List</a></li>
<li><a href="/forms/Internship_Student_Application.pdf" download="Internship Student Application.pdf">Internship Student Application Form</a> (191KB PDF Savable/Printable Form)</li>
<li><a href="/forms/Internship_Parent_Permission.pdf">Internship Parent Permission Form</a> (189KB PDF)</li>
<li><a href="/forms/Internship_Parent_Permission.pdf" download="Internship Parent Permssion.pdf">Internship Parent Permission Form</a> (189KB PDF)</li>
<li><a href="/forms/Internship_Sponsors_Job_Description.pdf" download="Internship Sponsors Job Description.pdf">Internship Sponsors Job Description Form</a> (187KB PDF Savable/Printable Form)</li>
</ul>
</div>

View File

@@ -1,23 +1,42 @@
<template name="PhotoGallery">
<div class="textView" id="photoGalleryView">
<div>
<img class="pageHeaderPicture" src="./images/buckeye_v5.jpg">
<h3 class="photoGallery">Photo Gallery</h3>
<p class="photoContent">The purpose of this Photo Gallery is to share and celebrate the activities of Anderson Valley's students and the community that supports them. Since many student events occur either within school classrooms or outside of the valley during field trips, they are often known only to those who are directly involved. Through this Photo Gallery we hope to overcome this barrier and enhance the connections between all members of our community. The photos included in the gallery will change periodically. The goal is to capture current or recent events. We encourage all members of the community to contact us with ideas and images for this section of the web site. The photos have been divided into three categories. Selection of one of the following links will launch a slide show. Arrows on the page provide control of the show.</p>
<div class="galleryLinks">
<div class="galleryLink clickable noselect">Introduction</div>
{{#each slideshows}}
<div class="galleryLink clickable noselect" data-slideshow-id="{{_id}}">{{name}}</div>
{{/each}}
</div>
<h3 class="slideShows" style="clear: left; padding-left: 8px; margin-bottom: 0;">Slide Shows</h3>
<div class="galleryContainer" style="margin: 0 auto;">
<div class="galleryLinks">
<a href="" ng-click="start('gallery/community');">COMMUNITY &nbsp;</a> <a href="" ng-click="start('gallery/avhs');">AVHS &nbsp;</a> <a href="" ng-click="start('gallery/aves');">AVES &nbsp;</a>
{{#if slideshow}}
{{> Slideshow slideshow._id}}
{{else}}
<div class="editablePage">
{{{editableHTML}}}
<!-- This prevents the text from being smaller than the image and the footer from being shoved next to the image. -->
<div style="clear:both"></div>
</div>
<div id="slidesAndControls" style="display: none">
<div id="slides" class="slideShowContainer">
</div>
<div class="galleryNavigation">
<a href="" id="previous-navigation" ng-click="manipulate('prev');"><img src="images/nav-left.png"></a>
<a href="" id="next-navigation" ng-click="manipulate('next');"><img src="images/nav-right.png"></a>
</div>
{{/if}}
<!--<div>-->
<!--<img class="pageHeaderPicture" src="./images/buckeye_v5.jpg">-->
<!--<h3 class="photoGallery">Photo Gallery</h3>-->
<!--<p class="photoContent">The purpose of this Photo Gallery is to share and celebrate the activities of Anderson Valley's students and the community that supports them. Since many student events occur either within school classrooms or outside of the valley during field trips, they are often known only to those who are directly involved. Through this Photo Gallery we hope to overcome this barrier and enhance the connections between all members of our community. The photos included in the gallery will change periodically. The goal is to capture current or recent events. We encourage all members of the community to contact us with ideas and images for this section of the web site. The photos have been divided into three categories. Selection of one of the following links will launch a slide show. Arrows on the page provide control of the show.</p>-->
<!--</div>-->
<!--<h3 class="slideShows" style="clear: left; padding-left: 8px; margin-bottom: 0;">Slide Shows</h3>-->
</div>
</template>
<template name="Slideshow">
<div class="galleryContainer" style="margin: 0 auto;">
<div class="slidesAndControls">
<div class="slideShowContainer">
{{#each slides}}
<div class="slide{{#unless @index}} showSlide{{/unless}}" style="background-image: url('/slideshow-image/{{this}}')"></div>
{{/each}}
</div>
<div class="galleryNavigation">
<i class="fa fa-angle-left previous clickable noselect" aria-hidden="true"></i>
<i class="fa fa-angle-right next clickable noselect" aria-hidden="true"></i>
</div>
</div>
</div>
</template>
</template>

View File

@@ -1,30 +1,34 @@
#photoGalleryView
.textView
margin: 6px auto 0 auto
border: 1px solid #DDD
padding: 0 20px
max-width: 960px
.pageHeaderPicture
float: left
width: 350px
padding: 0 10px 5px 0
.galleryLinks a
font-family: Palatino,"Palatino Linotype","Palatino LT STD","Book Antiqua",Georgia,serif
font-size: 16px
font-weight: bold
text-decoration: none
color: rgb(34, 102, 153)
.photoGallery
.intro
display: block
.galleryLink
display: inline-block
padding-right: 20px
font-family: "Arial Black", "Arial Bold", Gadget, sans-serif
font-size: 19px
text-transform: uppercase
background-color: #EEE
.slideShows
font-family: "Arial Black", "Arial Bold", Gadget, sans-serif
font-size: 19px
text-transform: uppercase
background-color: #EEE
.photoContent
font-family: Palatino, "Palatino Linotype", "Palatino LT STD", "Book Antiqua", Georgia, serif
font-size: 16px
margin: 16px 0
.slideShowContainer
display: block
width: 400px
height: 400px
position: relative
.slide
display: none
position: absolute
top: 0
left: 0
right: 0
bottom: 0
background-position: 50% 100%
background-repeat:no-repeat
background-size: contain
.slide.showSlide
display: block
.galleryNavigation
display: block
width: 400px
text-align: center
.next, .previous
font-size: 26px
line-height: 26px
padding: 6px

View File

@@ -1,2 +1,109 @@
import './PhotoGallery.html';
import './PhotoGallery.html';
let PREFIX = "PhotoGallery_";
Tracker.autorun(function() {
Meteor.subscribe("pages");
Meteor.subscribe("slideshow");
});
Template.PhotoGallery.onCreated(function() {
let template = this;
this.slideshows = Meteor.collections.Slideshow.find({}, {sort: {name: 1}});
});
Template.PhotoGallery.events({
'click .galleryLink': function(event, template) {
let slideshowId = $(event.target).data("slideshow-id");
let slideshow = slideshowId ? Meteor.collections.Slideshow.findOne(slideshowId) : undefined;
Session.set(PREFIX + 'selectedSlideshow', slideshow);
}
});
Template.PhotoGallery.helpers({
editableHTML: function() {
let doc = Meteor.collections.Pages.findOne({name: "Slideshow"});
return doc === undefined ? "" : doc.html;
},
slideshows: function() {
return Template.instance().slideshows;
},
slideshow: function() {
return Session.get(PREFIX + "selectedSlideshow");
}
});
Template.Slideshow.onRendered(function() {
let template = this;
this.slideTimer = Meteor.setInterval(function() {
let current = template.$('.slide.showSlide');
if(current) {
let next = current.next();
if(next.length === 0) {
next = template.$('.slide:first');
}
current.removeClass('showSlide');
next.addClass('showSlide');
}
else {
let first = template.$('.slide:first');
if(first) first.addClass('showSlide');
}
}, 6000);
});
Template.Slideshow.onDestroyed(function() {
if(this.slideTimer) Meteor.clearTimeout(this.slideTimer);
});
Template.Slideshow.helpers({
slides: function() {
return Session.get(PREFIX + "selectedSlideshow").images;
}
});
Template.Slideshow.events({
"click .next": function(event, template) {
let current = template.$('.slide.showSlide');
if(current) {
let next = current.next();
if(next.length === 0) {
next = template.$('.slide:first');
}
current.removeClass('showSlide');
next.addClass('showSlide');
}
else {
let first = template.$('.slide:first');
if(first) first.addClass('showSlide');
}
},
"click .previous": function(event, template) {
let current = template.$('.slide.showSlide');
if(current) {
let previous = current.prev();
if(previous.length === 0) {
previous = template.$('.slide:last');
}
current.removeClass('showSlide');
previous.addClass('showSlide');
}
else {
let last = template.$('.slide:last');
if(last) last.addClass('showSlide');
}
}
});

View File

@@ -1,7 +1,7 @@
<template name="Scholarships">
<div id="scholarshipView" class="textView">
<div id="scholarshipDescription">
<img class="pageHeaderPicture" src="./images/maple_v5.jpg">
<img class="pageHeaderPicture" src="./images/scholarships.jpg">
<h3 class="scholarshipProgram">Scholarship Program</h3>
<p class="scholarshipProgramContent">Each June AVEF awards scholarships to AVHS graduating seniors who have applied to attend a college, university or vocational school. Scholarships are awarded on the basis of GPA, SAT scores, financial need, educational and personal goals and quality of the essay submitted with an application. In addition references, awards, work experience, community service, and other criteria are taken into account. The Scholarship Application Form is available below. The Scholarship Selection Criteria form used by the Scholarship Committee to evaluate applicants is also available below. In order to qualify a student's application and all supporting documents and letters must be received by AVEF on or before the deadline listed under the "Important Dates" tab on this web site.</p>
</div>

View File

@@ -0,0 +1,57 @@
<template name="SelectImageDialog">
<div id="selectImageDialog" class="modalContent">
<!--<span class="modalClose">&times;</span>-->
<div class="dialog">
<!--<span class="modalClose close">&times;</span>-->
<div class="leftControls">
<div class="controlContainer" style="visibility: {{#if image}}visible{{else}}hidden{{/if}}">
<h1>Original Size</h1>
<div>Width: {{originalWidth}}, Height: {{originalHeight}}</div>
<h1>Width</h1>
<div>
<a class="leftArrow widthLess"><i class="fa fa-angle-left" aria-hidden="true"></i></a>
<input class="number width" type="number" min="1" value="{{currentWidth}}"/>
<a class="rightArrow widthMore"><i class="fa fa-angle-right" aria-hidden="true"></i></a>
</div>
<h1>Height</h1>
<div>
<a class="leftArrow heightLess"><i class="fa fa-angle-left" aria-hidden="true"></i></a>
<input class="number height" type="number" min="1" value="{{currentHeight}}"/>
<a class="rightArrow heightMore"><i class="fa fa-angle-right" aria-hidden="true"></i></a>
</div>
<h1>Compression Level</h1>
<div>
<a class="leftArrow compressionLess"><i class="fa fa-angle-left" aria-hidden="true"></i></a>
<input class="number compression" type="number" min="0" max="10" value="{{currentCompression}}"/>
<a class="rightArrow compressionMore"><i class="fa fa-angle-right" aria-hidden="true"></i></a>
</div>
<div>
<label class="checkContainer">
<input class="lossyCompression" type="checkbox" checked="{{#if lossyCompression}}checked{{else}}{{/if}}"/>
<i class="fa fa-square-o unchecked"></i>
<i class="fa fa-check-square-o checked"></i>
Lossy Compression
</label>
</div>
<div>
Size Estimate: {{compressedSize}}
</div>
<div>
<label class="checkContainer">
<input class="previewCompression" type="checkbox" checked="{{#if previewCompression}}checked{{else}}{{/if}}"/>
<i class="fa fa-square-o unchecked"></i>
<i class="fa fa-check-square-o checked"></i>
Preview Compressed Image
</label>
</div>
</div>
</div>
<div class="canvasContainer">
<div class="canvasWrapper"><canvas class="insertImageCanvas empty"></canvas></div>
<a class="close"><i class="fa fa-times"></i></a>
<a class="select" style="visibility: {{#if image}}visible{{else}}hidden{{/if}}"><i class="fa fa-check"></i></a>
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,118 @@
#selectImageDialog
width: 80%
height: 80%
.dialog
display: table
//min-height: 320px
//height: auto
overflow: hidden
height: 100%
width: 100%
.leftControls
display: table-cell
vertical-align: top
background: #EEE
height: 100%
min-height: 320px
min-width: 120px
-webkit-box-shadow: 2px 0 5px -2px rgba(0,0,0,0.65)
-moz-box-shadow: 2px 0 5px -2px rgba(0,0,0,0.65)
box-shadow: 2px 0 5px -2px rgba(0,0,0,0.65)
h1
font-size: 12px
font-weight: 800
font-name: Arial, "Sans Serif"
text-decoration: none
text-transform: uppercase
background-color: #7babab
padding-top: 1px
white-space: nowrap
div
white-space: nowrap
input::-webkit-outer-spin-button, input::-webkit-inner-spin-button
//display: none; <- Crashes Chrome on hover
-webkit-appearance: none;
margin: 0 //Apparently some margin are still there even though it's hidden
label.checkContainer
line-height: 14px
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
input[type='checkbox'], .checked
display: none
input[type='checkbox']:checked ~ .checked
display: inline-block
input[type='checkbox']:checked ~ .unchecked
display: none
.unchecked
margin-right: 2px
.number
display: inline-block
width: 100px
user-select: none;
.leftArrow, .rightArrow
display: inline-block
//width: 20px
padding: 0 4px
margin: 0 5px
cursor: pointer
text-decoration: none
border: 0
border-radius: 8px
user-select: none;
.leftArrow:hover, .rightArrow:hover
background-color: white
.leftArrow:active, .rightArrow:active
background-color: #999
.canvasContainer
display: table-cell
vertical-align: top
height: auto
width: 100%
padding-left: 4px
.canvasWrapper
position: relative
width: 100%
height: 100%
overflow: auto
.insertImageCanvas
position: absolute
left: 0
right: 0
top: 0
bottom: 0
display: block
border: 1px solid #888
cursor: pointer
//Setting a max causes the canvas to scale automatically
//max-height: 100%
//max-width: 100%
.insertImageCanvas.empty
border: 3px dashed #444
border-radius: 10px
margin: 20px 20px
.close, .select
position: absolute
top: 6px
height: 30px
width: 30px
background-color: #BBB
border: 1px solid #999
border-radius: 8px
line-height 30px
text-align: center
cursor: pointer
.close
right: 20px
color: #A31
.select
right: 56px
color: #072
.close:hover, .select:hover
background-color: #EEE
.close:active, .select:active
background-color: #999

View File

@@ -0,0 +1,382 @@
import './SelectImageDialog.html';
Template.SelectImageDialog.onCreated(function() {
this.originalImage = new ReactiveVar(undefined); //The original image. This is used to regenerate the displayed image after altering the rendering pipeline.
this.currentWidth = new ReactiveVar(0); //The current width of the image. Must be > 0 and <= the original image width. The aspect ratio must be maintained (there is no reason to ever allow a change in aspect ratio).
this.currentHeight = new ReactiveVar(0); //The current height of the image. Must be > 0 and <= the original image height. The aspect ratio must be maintained (there is no reason to ever allow a change in aspect ratio).
this.currentCompression = new ReactiveVar(8); //On a range of 0..10 where 10 is maximum, and zero is none.
this.lossyCompression = new ReactiveVar(true); //Boolean value indicating whether lossy compression should be utilized to reduce image sizes.
this.compressedSize = new ReactiveVar(0); //The current display image's size once compressed (in base64 units). Note we could start saving the image outside the html (non-embeded), in which case the image size would be reduced by 1/4 roughly.
this.previewCompression = new ReactiveVar(false); //Whether the viewed image includes the compression.
this.convertedImage = undefined;
});
Template.SelectImageDialog.onRendered(function() {
let template = this;
//TODO: Setup Jimp to edit the image in the canvas
let $canvas = template.$('.insertImageCanvas');
let canvas = $canvas[0];
let context = canvas.getContext('2d');
canvas.height = 300;
canvas.width = 300;
template.readFile = function(file) {
let reader = new FileReader();
reader.onload = function(e) {
let image = document.createElement("img");
image.addEventListener("load", function() {
//canvas.height = image.height;
//canvas.width = image.width;
//$canvas.css({width: image.width, height: image.height});
template.currentHeight.set(image.height);
template.currentWidth.set(image.width);
//context.clearRect(0, 0, canvas.width, canvas.height);
//context.drawImage(image, 0, 0);
$canvas.removeClass("empty");
template.originalImage.set(image);
template.rerender();
}, false);
image.src = e.target.result;
//var id = 'blobid' + (new Date()).getTime();
//var blobCache = tinymce.activeEditor.editorUpload.blobCache;
//var base64 = reader.result.split(',')[1];
//var blobInfo = blobCache.create(id, file, base64);
//blobCache.add(blobInfo);
//
//// call the callback and populate the Title field with the file name
//cb(blobInfo.blobUri(), { title: file.name });
};
reader.readAsDataURL(file);
};
context.font = '10pt Arial, Sans Serif';
context.fillStyle = 'red';
context.textAlign = 'center';
context.fillText("Drag & Drop", canvas.width / 2, canvas.height / 2 - 14);
context.fillStyle = '#666';
context.fillText("or", canvas.width / 2, canvas.height / 2);
context.fillStyle = 'red';
context.fillText("Click To Add Image", canvas.width / 2, canvas.height / 2 + 16);
template.changeWidth = function(delta) {
let originalImage = template.originalImage.get();
let ratio = originalImage.height / originalImage.width;
let width = template.currentWidth.get() + delta;
let height = 0;
if(width < 0) width = 1;
else if(width > originalImage.width) width = originalImage.width;
height = Math.round(ratio * width);
template.setSize(width, height <= 0 ? 1 : height);
};
template.changeHeight = function(delta) {
let originalImage = template.originalImage.get();
let ratio = originalImage.width / originalImage.height;
let height = template.currentHeight.get() + delta;
let width;
if(height < 0) height = 1;
else if(height > originalImage.height) height = originalImage.height;
width = Math.round(ratio * height);
template.setSize(width <= 0 ? 1 : width, height);
};
template.setSize = function(width, height) {
template.currentWidth.set(width);
template.currentHeight.set(height);
template.rerender();
};
template.rerender = function() {
let image = template.originalImage.get();
//Apply the width/height and other changes to the original image and render to the canvas.
canvas.height = image.height;
canvas.width = image.width;
$canvas.css({width: image.width, height: image.height});
context.clearRect(0, 0, canvas.width, canvas.height);
context.drawImage(image, 0, 0);
//Apply filters
//Resize
if(template.currentWidth.get() !== image.width && template.currentHeight.get() !== image.height) {
let width = template.currentWidth.get();
let height = template.currentHeight.get();
template.resizeCanvas(canvas, width, height, true);
$canvas.css({width: width, height: height});
}
//If the user has requested that the display show the compression then get the compressed canvas contents and re-display them in the canvas such that the compression is shown.
if(template.previewCompression.get()) {
let image = new Image();
//Save the converted image such that we don't compress an already compressed image if the user saves the changes.
template.convertedImage = getImageFromCanvas();
//Use an image object to convert the compressed image into something we can render to the canvas.
image.onload = function() {
context.drawImage(image, 0, 0);
};
image.src = template.convertedImage;
}
else {
template.convertedImage = undefined;
}
template.collectStatistics();
};
function getImageFromCanvas() { //Gets the base64 string containing the image with selected compression and in the desired format.
let compression = Math.abs(template.currentCompression.get() -10) / 10;
let type = template.lossyCompression.get() ? "image/jpeg" : "image/png";
return canvas.toDataURL(type, compression);
}
template.saveImage = function() {
let dataURL = template.convertedImage ? template.convertedImage : getImageFromCanvas();
let data = Template.currentData();
if(data && data.onApply && typeof data.onApply === 'function') {
data.onApply(dataURL);
}
};
template.collectStatistics = function() {
let imageData = getImageFromCanvas();
//Save the compressed base64 size. If we were to allow the image to be saved outside the html (non-embedded) then we should multiply this by 0.75 for an estimate of a binary format.
//Convert to kilo bytes and ensure the value is at least 1kb (it would be weird to have zero kb files).
template.compressedSize.set(Math.max(1, Math.round(imageData.length / 1000)) + "kb");
};
template.resizeCanvas = function(canvas, width, height, resizeCanvas) {
let width_source = canvas.width;
let height_source = canvas.height;
width = Math.round(width);
height = Math.round(height);
let ratio_w = width_source / width;
let ratio_h = height_source / height;
let ratio_w_half = Math.ceil(ratio_w / 2);
let ratio_h_half = Math.ceil(ratio_h / 2);
let ctx = canvas.getContext("2d");
let img = ctx.getImageData(0, 0, width_source, height_source);
let img2 = ctx.createImageData(width, height);
let data = img.data;
let data2 = img2.data;
for(let j = 0; j < height; j++) {
for(let i = 0; i < width; i++) {
let x2 = (i + j * width) * 4;
let weight = 0;
let weights = 0;
let weights_alpha = 0;
let gx_r = 0;
let gx_g = 0;
let gx_b = 0;
let gx_a = 0;
let center_y = (j + 0.5) * ratio_h;
let yy_start = Math.floor(j * ratio_h);
let yy_stop = Math.ceil((j + 1) * ratio_h);
for (let yy = yy_start; yy < yy_stop; yy++) {
let dy = Math.abs(center_y - (yy + 0.5)) / ratio_h_half;
let center_x = (i + 0.5) * ratio_w;
let w0 = dy * dy; //pre-calc part of w
let xx_start = Math.floor(i * ratio_w);
let xx_stop = Math.ceil((i + 1) * ratio_w);
for (let xx = xx_start; xx < xx_stop; xx++) {
let dx = Math.abs(center_x - (xx + 0.5)) / ratio_w_half;
let w = Math.sqrt(w0 + dx * dx);
if (w >= 1) {
//pixel too far
continue;
}
//hermite filter
weight = 2 * w * w * w - 3 * w * w + 1;
let pos_x = 4 * (xx + yy * width_source);
//alpha
gx_a += weight * data[pos_x + 3];
weights_alpha += weight;
//colors
if (data[pos_x + 3] < 255)
weight = weight * data[pos_x + 3] / 250;
gx_r += weight * data[pos_x];
gx_g += weight * data[pos_x + 1];
gx_b += weight * data[pos_x + 2];
weights += weight;
}
}
data2[x2] = gx_r / weights;
data2[x2 + 1] = gx_g / weights;
data2[x2 + 2] = gx_b / weights;
data2[x2 + 3] = gx_a / weights_alpha;
}
}
//clear and resize canvas
if(resizeCanvas) {
canvas.width = width;
canvas.height = height;
} else {
ctx.clearRect(0, 0, width_source, height_source);
}
//draw
ctx.putImageData(img2, 0, 0);
};
});
Template.SelectImageDialog.events({
'click .close': function(event, template) {
let data = Template.currentData();
if(data && data.onClose && typeof data.onClose === 'function') {
data.onClose();
}
},
'mouseup .insertImageCanvas': function(event, template) {
let input = document.createElement('input');
input.setAttribute('type', 'file');
input.setAttribute('accept', 'image/*');
input.onchange = function() {
template.readFile(this.files[0]);
};
input.click();
},
'click .select': function(event, template) {
template.saveImage();
},
'focusout .width': function(event, template) {
let width = parseInt(event.target.value);
if(width !== template.currentWidth.get()) template.changeWidth(event.target.value - template.currentWidth.get());
},
'keypress .width': function(event, template) {
if(event.which === 13) {
let width = parseInt(event.target.value);
if(width !== template.currentWidth.get()) template.changeWidth(event.target.value - template.currentWidth.get());
}
},
'click .widthLess': function(event, template) {
template.changeWidth(-1);
},
'click .widthMore': function(event, template) {
template.changeWidth(1);
},
'focusout .height': function(event, template) {
let height = parseInt(event.target.value);
if(height !== template.currentHeight.get()) template.changeHeight(height - template.currentHeight.get());
},
'keypress .height': function(event, template) {
if(event.which === 13) {
let height = parseInt(event.target.value);
if(height !== template.currentHeight.get()) template.changeHeight(height - template.currentHeight.get());
}
},
'click .heightLess': function(event, template) {
template.changeHeight(-1);
},
'click .heightMore': function(event, template) {
template.changeHeight(1);
},
'dragover .insertImageCanvas': function(event, template) {
event.preventDefault();
},
'drop .insertImageCanvas': function(event, template) {
event.preventDefault();
let files = event.originalEvent.dataTransfer.files;
if(files.length > 0) {
let file = files[0];
if(typeof FileReader !== 'undefined' && file.type.indexOf("image") !== -1) {
template.readFile(file);
}
}
},
'focusout .compression': function(event, template) {
let compression = parseInt(event.target.value);
if(compression !== template.currentCompression.get()) {
template.currentCompression.set(compression >= 0 ? (compression <= 10 ? compression : 10) : 0);
template.collectStatistics();
}
},
'keypress .compression': function(event, template) {
if(event.which === 13) {
let compression = parseInt(event.target.value);
if(compression !== template.currentCompress.get()) {
template.currentCompression.set(compression >= 0 ? (compression <= 10 ? compression : 10) : 0);
if(template.previewCompression.get()) template.rerender();
else template.collectStatistics();
}
}
},
'click .compressionLess': function(event, template) {
let compression = template.currentCompression.get() - 1;
template.currentCompression.set(compression >= 0 ? compression : 0);
if(template.previewCompression.get()) template.rerender();
else template.collectStatistics();
},
'click .compressionMore': function(event, template) {
let compression = template.currentCompression.get() + 1;
template.currentCompression.set(compression <= 10 ? compression : 10);
if(template.previewCompression.get()) template.rerender();
else template.collectStatistics();
},
'change .lossyCompression': function(event, template) {
template.lossyCompression.set(event.target.checked);
template.collectStatistics();
},
'change .previewCompression': function(event, template) {
template.previewCompression.set(event.target.checked);
template.rerender();
}
});
Template.SelectImageDialog.helpers({
image: function() {
return Template.instance().originalImage.get();
},
originalWidth: function() {
let image = Template.instance().originalImage.get();
return image ? image.width : 0;
},
originalHeight: function() {
let image = Template.instance().originalImage.get();
return image ? image.height : 0;
},
currentWidth: function() {
return Template.instance().currentWidth.get();
},
currentHeight: function() {
return Template.instance().currentHeight.get();
},
currentCompression() {
return Template.instance().currentCompression.get();
},
lossyCompression() {
return Template.instance().lossyCompression.get();
},
compressedSize() {
return Template.instance().compressedSize.get();
},
previewCompression() {
return Template.instance().previewCompression.get();
}
});

View File

@@ -15,6 +15,16 @@
Internship Job List
</a>
</li>
<li class="{{isActiveRoute 'SlideshowEditor'}}">
<a href="{{pathFor 'SlideshowEditor'}}">
Slideshows
</a>
</li>
<li class="{{isActiveRoute 'SlideshowPageEditor'}}">
<a href="{{pathFor 'SlideshowPageEditor'}}">
Slideshow Page
</a>
</li>
<li class="{{isActiveRoute 'DatesEditor'}}">
<a href="{{pathFor 'DatesEditor'}}">
Important Dates

View File

@@ -14,6 +14,9 @@ Template.Admin.toggleMenu = function($sidebar) {
}
};
Template.Admin.helpers({
});
Template.Admin.events({
"click .signOut": function(event, template) {
AccountsTemplates.logout();

29
imports/ui/styles/modal.import.styl vendored Normal file
View File

@@ -0,0 +1,29 @@
.modalBackground
display: block
position: fixed
z-index: 1000
left: 0
top: 0
width: 100%
height: 100%
overflow: auto
background-color: #000
background-color: rgba(0,0,0,0.4)
.modalContent
background-color: #fefefe
border: 1px solid #888
position: fixed
top: 50%
left: 50%
transform: translate(-50%, -50%)
.modalClose
color: #aaa
float: right
font-size: 28px
font-weight: 800
.modalClose:hover, modalClose.focus
color: black
text-decoration: none
cursor: pointer