.
diff --git a/system/application/views/widgets/ckeditor/plugins/scalarfootnotes/README.md b/system/application/views/widgets/ckeditor/plugins/scalarfootnotes/README.md
new file mode 100644
index 000000000..95d8d2345
--- /dev/null
+++ b/system/application/views/widgets/ckeditor/plugins/scalarfootnotes/README.md
@@ -0,0 +1,2 @@
+# scalarfootnotes
+Manages footnotes for the Scalar WYSIWYG
diff --git a/system/application/views/widgets/ckeditor/plugins/scalarfootnotes/dialogs/scalarfootnotes.js b/system/application/views/widgets/ckeditor/plugins/scalarfootnotes/dialogs/scalarfootnotes.js
new file mode 100644
index 000000000..97b681d33
--- /dev/null
+++ b/system/application/views/widgets/ckeditor/plugins/scalarfootnotes/dialogs/scalarfootnotes.js
@@ -0,0 +1,110 @@
+/**
+ * The scalarfootnotes dialogue definition
+ * Version 0.1.0-mvp
+ * https://github.com/UIUCLibrary/scalarfootnotes
+ */
+
+(function($) {
+ "use strict";
+
+ CKEDITOR.dialog.add( 'scalarfootnotesDialog', function( editor ) {
+
+ return {
+ editor_name: false,
+ scalarfootnotes_editor: false,
+ dialog_dom_id: false,
+ // Basic properties of the dialog window: title, minimum size.
+ title: 'Add Footnotes',
+ minWidth: 400,
+ minHeight: 200,
+ scalarfootnotes_el: false,
+
+ // Dialog window contents definition.
+ //TODO: add tab to configure scalarfootnotes
+ contents: [
+ {
+ // Definition of the Basic Settings dialog tab (page).
+ id: 'tab-basic',
+ label: 'Basic Settings',
+
+ // The tab contents.
+ elements: [
+ {
+ // Text input field for the scalarfootnotes text.
+ type: 'textarea',
+ id: 'new_footnote',
+ 'class': 'footnote_text',
+ label: 'New footnote:',
+ inputStyle: 'height: 100px',
+ }
+ ]
+ },
+ ],
+
+ //Fires when dialog opens, instantiates the dialogue components
+ onShow: function () {
+ this.setupContent();
+ var dialog = this;
+
+ CKEDITOR.on( 'instanceLoaded', function( evt ) {
+ dialog.editor_name = evt.editor.name;
+ dialog.scalarfootnotes_editor = evt.editor;
+ } );
+ //keeping this becuase I like this position better
+ jQuery('.cke_dialog').css({'position': 'absolute', 'top': '2%'});
+
+ CKEDITOR.replaceAll( function( textarea, config ) {
+ // Make sure the textarea has the correct class:
+ //TODO: make this graceful
+ if (!textarea.className.match(/footnote_text/)) {
+ return false;
+ }
+
+ //add the elements to the toolbar for the footnote textarea
+ config.toolbar = [
+ { name: 'clipboard', items : ['Undo','Redo' ] },
+ { name: 'basicstyles', items : [ 'Bold','Italic'] },
+ { name: 'clear', items : [ 'RemoveFormat'] },
+ {name: 'link', items: ['Unlink', 'Scalar7']}
+ ]
+ config.allowedContent = 'br em strong; a[!href]';
+ config.enterMode = CKEDITOR.ENTER_BR;
+ config.autoParagraph = false;
+ config.height = 80;
+ config.resize_enabled = false;
+ config.autoGrow_minHeight = 80;
+
+ var extra_config = editor.config.scalarfootnotesDialogEditorExtraConfig;
+ if (extra_config) {
+ for (var attribute in extra_config) {
+ config[attribute] = extra_config[attribute];
+ }
+ }
+ return true;
+ });
+ },
+
+ //Fires when user hits Okay
+ onOk: function() {
+ var dialog = this;
+ var footnote_editor = CKEDITOR.instances[dialog.editor_name];
+ var footnote_data = footnote_editor.getData();
+
+ //TODO: need to make sure this is the correct type of check
+ if (footnote_data != '') {
+ editor.plugins.scalarfootnotes.build(footnote_data, editor);
+ }
+
+ // Destroy the editor so it's rebuilt properly next time:
+ footnote_editor.destroy();
+ return;
+ },
+
+ onCancel: function() {
+ var dialog = this;
+ var footnote_editor = CKEDITOR.instances[dialog.editor_name];
+ footnote_editor.destroy();
+ }
+ };
+ });
+}(window.jQuery));
diff --git a/system/application/views/widgets/ckeditor/plugins/scalarfootnotes/icons/scalarfootnotes.png b/system/application/views/widgets/ckeditor/plugins/scalarfootnotes/icons/scalarfootnotes.png
new file mode 100644
index 000000000..77157440b
Binary files /dev/null and b/system/application/views/widgets/ckeditor/plugins/scalarfootnotes/icons/scalarfootnotes.png differ
diff --git a/system/application/views/widgets/ckeditor/plugins/scalarfootnotes/plugin.js b/system/application/views/widgets/ckeditor/plugins/scalarfootnotes/plugin.js
new file mode 100644
index 000000000..ed0c67690
--- /dev/null
+++ b/system/application/views/widgets/ckeditor/plugins/scalarfootnotes/plugin.js
@@ -0,0 +1,264 @@
+/**
+ * Insert scalarfootnotes elements into CKEditor editing area.
+ *
+ * Version 0.1.0-mvp
+ * https://github.com/UIUCLibrary/scalarfootnotes
+ */
+
+(function($) {
+ 'use strict';
+
+ CKEDITOR.plugins.add('scalarfootnotes', {
+ footnote_ids: [],
+ requires: 'widget',
+ icons: 'scalarfootnotes',
+
+ init: function(editor) {
+
+ // Check for jQuery
+ // @TODO - remove if/when JQ dep. is removed.
+ if (typeof(window.jQuery) == 'undefined') {
+ console.warn('jQuery required but undetected so quitting scalarfootnotes.');
+ return false;
+ }
+
+ // Allow `cite` to be editable:
+ CKEDITOR.dtd.$editable['cite'] = 1;
+
+ // Add some CSS tweaks:
+ var css = '.scalarfootnotes{background:#eee; padding:1px 15px;} .scalarfootnotes cite{font-style: normal;}';
+ CKEDITOR.addCss(css);
+
+ var $this = this;
+
+ // Force a reorder on startup to make sure all vars are set: (e.g. footnotes store):
+ editor.on('instanceReady', function(evt) {
+ $this.reorderMarkers(editor);
+ });
+
+ // Add the reorder change event:
+ editor.on('change', function(evt) {
+ // Copy the scalarfootnotes_store as we may be doing a cut:
+ if(!evt.editor.scalarfootnotes_tmp) {
+ evt.editor.scalarfootnotes_tmp = evt.editor.scalarfootnotes_store;
+ }
+
+ // Prevent no selection errors:
+ if (!evt.editor.getSelection().getStartElement()) {
+ return;
+ }
+
+ // SetTimeout seems to be necessary (it's used in the core but can't be 100% sure why)
+ setTimeout(function(){
+ $this.reorderMarkers(editor);
+ },
+ 0
+ );
+ });
+ // Build the initial scalarfootnotes widget editables definition:
+ var prefix = editor.config.scalarfootnotesPrefix ? '-' + editor.config.scalarfootnotesPrefix : '';
+ var def = {
+ header: {
+ selector: 'header > *',
+ //allowedContent: ''
+ allowedContent: 'strong em span sub sup;'
+ }
+ };
+ var contents = $('' + editor.element.$.textContent + '
')
+ , l = contents.find('.scalarfootnotes li').length
+ , i = 1;
+ for (i; i <= l; i++) {
+ def['footnote_' + i] = {selector: '#footnote' + prefix + '-' + i + ' cite', allowedContent: 'a[href]; cite[*](*); strong em span br'};
+ }
+
+ // Register the scalarfootnotes widget.
+ editor.widgets.add('scalarfootnotes', {
+
+ // Minimum HTML which is required by this widget to work.
+ requiredContent: 'div(scalarfootnotes)',
+
+ // Check the elements that need to be converted to widgets.
+ upcast: function(element) {
+ return element.name == 'div' && element.hasClass('scalarfootnotes');
+ },
+
+ editables: def
+ });
+
+ // Register the scalarfootnotes widget.
+ editor.widgets.add('scalarfootnotes', {
+
+ // Minimum HTML which is required by this widget to work.
+ requiredContent: 'sup[data-footnote-id]',
+
+ // Check the elements that need to be converted to widgets.
+ upcast: function(element) {
+ return element.name == 'sup' && element.attributes['data-footnote-id'] != 'undefined';
+ },
+ });
+
+ // Define an editor command that opens our dialog.
+ editor.addCommand('scalarfootnotes', new CKEDITOR.dialogCommand('scalarfootnotesDialog', {
+ // @TODO: This needs work:
+ allowedContent: 'div[*](*);header[*](*);li[*];a[*];cite(*)[*];sup[*]',
+ requiredContent: 'div[*](*);header[*](*);li[*];a[*];cite(*)[*];sup[*]'
+ }));
+
+ // Create a toolbar button that executes the above command.
+ editor.ui.addButton('scalarfootnotes', {
+
+ // The text part of the button (if available) and tooptip.
+ label: 'Insert Footnote',
+
+ // The command to execute on click.
+ command: 'scalarfootnotes',
+
+ // The button placement in the toolbar (toolbar group name).
+ toolbar: 'insert'
+ });
+
+ // Register our dialog file. this.path is the plugin folder path.
+ CKEDITOR.dialog.add('scalarfootnotesDialog', this.path + 'dialogs/scalarfootnotes.js');
+ },
+ build: function(footnote, editor) {
+ var footnote_id = this.generateFootnoteId();
+
+ // Insert the marker with dummy content:
+ var footnote_marker = 'X';
+
+ editor.insertHtml(footnote_marker);
+
+ editor.fire('lockSnapshot');
+ this.addFootnote(this.buildFootnote(footnote_id, footnote, false, editor), editor);
+ editor.fire('unlockSnapshot');
+
+ this.reorderMarkers(editor);
+ },
+
+ buildFootnote: function(footnote_id, footnote_text, data, editor) {
+ var link = '',
+ footnote,
+ order = data ? data.order.indexOf(footnote_id) + 1 : 1,
+ prefix = editor.config.scalarfootnotesPrefix ? '-' + editor.config.scalarfootnotesPrefix : '';
+ link = '↵ ';
+ footnote = '';
+ return footnote;
+ },
+
+ addFootnote: function(footnote, editor) {
+ var $contents = $(editor.editable().$);
+ var $scalarfootnotes = $contents.find('.scalarfootnotes');
+
+ //if this is the first footnote, build the footnotes section
+ if ($scalarfootnotes.length == 0) {
+ var header_title = editor.config.scalarfootnotesTitle ? editor.config.scalarfootnotesTitle : 'Footnotes';
+ var header_els = ['', '
'];//editor.config.editor.config.scalarfootnotesHeaderEls
+ if (editor.config.scalarfootnotesHeaderEls) {
+ header_els = editor.config.scalarfootnotesHeaderEls;
+ }
+ var container = '';
+ // Move cursor to end of content:
+ var range = editor.createRange();
+ range.moveToElementEditEnd(range.root);
+ editor.getSelection().selectRanges([range]);
+ // Insert the container:
+ editor.insertHtml(container);
+ } else {
+ $scalarfootnotes.find('ol').append(footnote);
+ }
+ },
+
+ generateFootnoteId: function() {
+ var id = Math.random().toString(36).substr(2, 5);
+ while ($.inArray(id, this.footnote_ids) != -1) {
+ id = String(this.generateFootnoteId());
+ }
+ this.footnote_ids.push(id);
+ return id;
+ },
+
+ reorderMarkers: function(editor) {
+ editor.fire('lockSnapshot');
+ var prefix = editor.config.scalarfootnotesPrefix ? '-' + editor.config.scalarfootnotesPrefix : '';
+ var $contents = $(editor.editable().$);
+ var data = {
+ order: []
+ };
+
+ // Check that there's a scalarfootnotes div. If it's been deleted the markers are useless:
+ if ($contents.find('.scalarfootnotes').length == 0) {
+ $contents.find('sup[data-footnote-id]').remove();
+ editor.fire('unlockSnapshot');
+ return;
+ }
+
+ // Find all the markers in the document:
+ var $markers = $contents.find('sup[data-footnote-id]');
+ // If there aren't any, remove the scalarfootnotes container:
+ if ($markers.length == 0) {
+ $contents.find('.scalarfootnotes').parent().remove();
+ editor.fire('unlockSnapshot');
+ return;
+ }
+
+ // Otherwise reorder the markers:
+ $markers.each(function(){
+ var footnote_id = $(this).attr('data-footnote-id')
+ , marker_ref;
+ data.order.push(footnote_id);
+ n = data.order.length;
+ marker_ref = n;
+
+ // Replace the marker contents:
+ var marker = '';
+ $(this).html(marker);
+ });
+
+ // Prepare the scalarfootnotes_store object:
+ editor.scalarfootnotes_store = {};
+
+ // Then rebuild the scalarfootnotes content to match marker order:
+ var scalarfootnotes = ''
+ , footnote_text = ''
+ , footnote_id
+ , i = 0
+ , l = data.order.length;
+ for (i; i < l; i++) {
+ footnote_id = data.order[i];
+ footnote_text = $contents.find('.scalarfootnotes [data-footnote-id=' + footnote_id + '] cite').html();
+ // If the scalarfootnotes text can't be found in the editor, it may be in the tmp store
+ // following a cut:
+ if (!footnote_text) {
+ footnote_text = editor.scalarfootnotes_tmp[footnote_id];
+ }
+ scalarfootnotes += this.buildFootnote(footnote_id, footnote_text, data, editor);
+ // Store the scalarfootnotes for later use (post cut/paste):
+ editor.scalarfootnotes_store[footnote_id] = footnote_text;
+ }
+
+ // Insert the scalarfootnotes into the list:
+ $contents.find('.scalarfootnotes ol').html(scalarfootnotes);
+
+ // Next we need to reinstate the 'editable' properties of the scalarfootnotes.
+ // (we have to do this individually due to Widgets 'fireOnce' for editable selectors)
+ var el = $contents.find('.scalarfootnotes')
+ , n
+ , footnote_widget;
+ // So first we need to find the right Widget instance:
+ // (I hope there's a better way of doing this but I can't find one)
+ for (i in editor.widgets.instances) {
+ if (editor.widgets.instances[i].name == 'scalarfootnotes') {
+ footnote_widget = editor.widgets.instances[i];
+ break;
+ }
+ }
+ // Then we `initEditable` each footnote, giving it a unique selector:
+ for (i in data.order) {
+ n = parseInt(i) + 1;
+ footnote_widget.initEditable('footnote_' + n, {selector: '#footnote' + prefix + '-' + n +' cite', allowedContent: 'a[href]; cite[*](*); em strong span'});
+ }
+
+ editor.fire('unlockSnapshot');
+ }
+ });
+}(window.jQuery));