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:
@@ -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>
|
||||
@@ -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)
|
||||
@@ -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!");
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -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>
|
||||
@@ -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!");
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -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>
|
||||
6
imports/ui/Admin/DatesEditor.import.styl
vendored
6
imports/ui/Admin/DatesEditor.import.styl
vendored
@@ -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)
|
||||
@@ -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
|
||||
33
imports/ui/Admin/InternshipEditor.html
Normal file
33
imports/ui/Admin/InternshipEditor.html
Normal 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>
|
||||
118
imports/ui/Admin/InternshipEditor.import.styl
vendored
Normal file
118
imports/ui/Admin/InternshipEditor.import.styl
vendored
Normal 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%
|
||||
228
imports/ui/Admin/InternshipEditor.js
Normal file
228
imports/ui/Admin/InternshipEditor.js
Normal 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: </strong></p>\n<p><strong>Supervisor / Sponsor: </strong></p>\n<p><strong>Location of Internship: </strong></p>\n<p><strong>Dates & hours: </strong></p>\n<p><strong>Duties & Activities: </strong></p>\n<p><strong>Desirable Qualities / Skills: </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;
|
||||
}
|
||||
});
|
||||
@@ -1,4 +0,0 @@
|
||||
<template name="InternshipsEditor">
|
||||
<div id="internshipsEditor">
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,2 +0,0 @@
|
||||
#internshipsEditor
|
||||
display: block
|
||||
@@ -1,2 +0,0 @@
|
||||
|
||||
import './InternshipsEditor.html';
|
||||
@@ -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>
|
||||
6
imports/ui/Admin/NewsEditor.import.styl
vendored
6
imports/ui/Admin/NewsEditor.import.styl
vendored
@@ -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)
|
||||
@@ -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!");
|
||||
}
|
||||
}
|
||||
});
|
||||
13
imports/ui/Admin/PageEditor.html
Normal file
13
imports/ui/Admin/PageEditor.html
Normal 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>
|
||||
226
imports/ui/Admin/PageEditor.js
Normal file
226
imports/ui/Admin/PageEditor.js
Normal 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);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
41
imports/ui/Admin/SlideshowEditor.html
Normal file
41
imports/ui/Admin/SlideshowEditor.html
Normal 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>
|
||||
|
||||
108
imports/ui/Admin/SlideshowEditor.import.styl
vendored
Normal file
108
imports/ui/Admin/SlideshowEditor.import.styl
vendored
Normal 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
|
||||
213
imports/ui/Admin/SlideshowEditor.js
Normal file
213
imports/ui/Admin/SlideshowEditor.js
Normal 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);
|
||||
});
|
||||
}
|
||||
});
|
||||
7
imports/ui/EditablePage.html
Normal file
7
imports/ui/EditablePage.html
Normal 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
32
imports/ui/EditablePage.import.styl
vendored
Normal 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
|
||||
33
imports/ui/EditablePage.js
Normal file
33
imports/ui/EditablePage.js
Normal 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;
|
||||
}
|
||||
});
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 & Notices</div>
|
||||
<div id="newsHeaderTimestampDiv" class="menuHeaderTimestamp">03/13/18</div>
|
||||
|
||||
@@ -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>
|
||||
2
imports/ui/InternshipJobs.import.styl
vendored
2
imports/ui/InternshipJobs.import.styl
vendored
@@ -1,2 +1,2 @@
|
||||
#internshipJobs
|
||||
margin: 20px 40px
|
||||
display: block
|
||||
@@ -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({
|
||||
});
|
||||
@@ -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>
|
||||
|
||||
@@ -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 </a> <a href="" ng-click="start('gallery/avhs');">AVHS </a> <a href="" ng-click="start('gallery/aves');">AVES </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>
|
||||
|
||||
56
imports/ui/PhotoGallery.import.styl
vendored
56
imports/ui/PhotoGallery.import.styl
vendored
@@ -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
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -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>
|
||||
|
||||
57
imports/ui/dialogs/SelectImageDialog.html
Normal file
57
imports/ui/dialogs/SelectImageDialog.html
Normal file
@@ -0,0 +1,57 @@
|
||||
|
||||
<template name="SelectImageDialog">
|
||||
<div id="selectImageDialog" class="modalContent">
|
||||
<!--<span class="modalClose">×</span>-->
|
||||
<div class="dialog">
|
||||
<!--<span class="modalClose close">×</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>
|
||||
118
imports/ui/dialogs/SelectImageDialog.import.styl
vendored
Normal file
118
imports/ui/dialogs/SelectImageDialog.import.styl
vendored
Normal 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
|
||||
382
imports/ui/dialogs/SelectImageDialog.js
Normal file
382
imports/ui/dialogs/SelectImageDialog.js
Normal 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();
|
||||
}
|
||||
});
|
||||
@@ -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
|
||||
|
||||
@@ -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
29
imports/ui/styles/modal.import.styl
vendored
Normal 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
|
||||
Reference in New Issue
Block a user