From e47e83cf6bded3d1924b4d500193e7876833ef83 Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Sun, 2 Jul 2017 13:03:43 -0400 Subject: Created admin panel Currently allows you to create and edit blogs, including associated records. Uses a WYSIWYG editor that allows uploading images. Also included jQuery :( --- .../ckeditor/plugins/image2/dialogs/image2.js | 553 +++++++++++++++++++++ 1 file changed, 553 insertions(+) create mode 100644 app/assets/javascripts/ckeditor/plugins/image2/dialogs/image2.js (limited to 'app/assets/javascripts/ckeditor/plugins/image2/dialogs') diff --git a/app/assets/javascripts/ckeditor/plugins/image2/dialogs/image2.js b/app/assets/javascripts/ckeditor/plugins/image2/dialogs/image2.js new file mode 100644 index 0000000..cb393a3 --- /dev/null +++ b/app/assets/javascripts/ckeditor/plugins/image2/dialogs/image2.js @@ -0,0 +1,553 @@ +/** + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +/** + * @fileOverview Image plugin based on Widgets API + */ + +'use strict'; + +CKEDITOR.dialog.add( 'image2', function( editor ) { + + // RegExp: 123, 123px, empty string "" + var regexGetSizeOrEmpty = /(^\s*(\d+)(px)?\s*$)|^$/i, + + lockButtonId = CKEDITOR.tools.getNextId(), + resetButtonId = CKEDITOR.tools.getNextId(), + + lang = editor.lang.image2, + commonLang = editor.lang.common, + + lockResetStyle = 'margin-top:18px;width:40px;height:20px;', + lockResetHtml = new CKEDITOR.template( + '
' + + '' + + '' + + '' + lang.lockRatio + '' + + '' + + + '' + + '' + lang.resetSize + '' + + '' + + '
' ).output( { + lockButtonId: lockButtonId, + resetButtonId: resetButtonId + } ), + + helpers = CKEDITOR.plugins.image2, + + // Editor instance configuration. + config = editor.config, + + hasFileBrowser = !!( config.filebrowserImageBrowseUrl || config.filebrowserBrowseUrl ), + + // Content restrictions defined by the widget which + // impact on dialog structure and presence of fields. + features = editor.widgets.registered.image.features, + + // Functions inherited from image2 plugin. + getNatural = helpers.getNatural, + + // Global variables referring to the dialog's context. + doc, widget, image, + + // Global variable referring to this dialog's image pre-loader. + preLoader, + + // Global variables holding the original size of the image. + domWidth, domHeight, + + // Global variables related to image pre-loading. + preLoadedWidth, preLoadedHeight, srcChanged, + + // Global variables related to size locking. + lockRatio, userDefinedLock, + + // Global variables referring to dialog fields and elements. + lockButton, resetButton, widthField, heightField, + + natural; + + // Validates dimension. Allowed values are: + // "123px", "123", "" (empty string) + function validateDimension() { + var match = this.getValue().match( regexGetSizeOrEmpty ), + isValid = !!( match && parseInt( match[ 1 ], 10 ) !== 0 ); + + if ( !isValid ) + alert( commonLang[ 'invalid' + CKEDITOR.tools.capitalize( this.id ) ] ); // jshint ignore:line + + return isValid; + } + + // Creates a function that pre-loads images. The callback function passes + // [image, width, height] or null if loading failed. + // + // @returns {Function} + function createPreLoader() { + var image = doc.createElement( 'img' ), + listeners = []; + + function addListener( event, callback ) { + listeners.push( image.once( event, function( evt ) { + removeListeners(); + callback( evt ); + } ) ); + } + + function removeListeners() { + var l; + + while ( ( l = listeners.pop() ) ) + l.removeListener(); + } + + // @param {String} src. + // @param {Function} callback. + return function( src, callback, scope ) { + addListener( 'load', function() { + // Don't use image.$.(width|height) since it's buggy in IE9-10 (http://dev.ckeditor.com/ticket/11159) + var dimensions = getNatural( image ); + + callback.call( scope, image, dimensions.width, dimensions.height ); + } ); + + addListener( 'error', function() { + callback( null ); + } ); + + addListener( 'abort', function() { + callback( null ); + } ); + + image.setAttribute( 'src', + ( config.baseHref || '' ) + src + '?' + Math.random().toString( 16 ).substring( 2 ) ); + }; + } + + // This function updates width and height fields once the + // "src" field is altered. Along with dimensions, also the + // dimensions lock is adjusted. + function onChangeSrc() { + var value = this.getValue(); + + toggleDimensions( false ); + + // Remember that src is different than default. + if ( value !== widget.data.src ) { + // Update dimensions of the image once it's preloaded. + preLoader( value, function( image, width, height ) { + // Re-enable width and height fields. + toggleDimensions( true ); + + // There was problem loading the image. Unlock ratio. + if ( !image ) + return toggleLockRatio( false ); + + // Fill width field with the width of the new image. + widthField.setValue( editor.config.image2_prefillDimensions === false ? 0 : width ); + + // Fill height field with the height of the new image. + heightField.setValue( editor.config.image2_prefillDimensions === false ? 0 : height ); + + // Cache the new width. + preLoadedWidth = width; + + // Cache the new height. + preLoadedHeight = height; + + // Check for new lock value if image exist. + toggleLockRatio( helpers.checkHasNaturalRatio( image ) ); + } ); + + srcChanged = true; + } + + // Value is the same as in widget data but is was + // modified back in time. Roll back dimensions when restoring + // default src. + else if ( srcChanged ) { + // Re-enable width and height fields. + toggleDimensions( true ); + + // Restore width field with cached width. + widthField.setValue( domWidth ); + + // Restore height field with cached height. + heightField.setValue( domHeight ); + + // Src equals default one back again. + srcChanged = false; + } + + // Value is the same as in widget data and it hadn't + // been modified. + else { + // Re-enable width and height fields. + toggleDimensions( true ); + } + } + + function onChangeDimension() { + // If ratio is un-locked, then we don't care what's next. + if ( !lockRatio ) + return; + + var value = this.getValue(); + + // No reason to auto-scale or unlock if the field is empty. + if ( !value ) + return; + + // If the value of the field is invalid (e.g. with %), unlock ratio. + if ( !value.match( regexGetSizeOrEmpty ) ) + toggleLockRatio( false ); + + // No automatic re-scale when dimension is '0'. + if ( value === '0' ) + return; + + var isWidth = this.id == 'width', + // If dialog opened for the new image, domWidth and domHeight + // will be empty. Use dimensions from pre-loader in such case instead. + width = domWidth || preLoadedWidth, + height = domHeight || preLoadedHeight; + + // If changing width, then auto-scale height. + if ( isWidth ) + value = Math.round( height * ( value / width ) ); + + // If changing height, then auto-scale width. + else + value = Math.round( width * ( value / height ) ); + + // If the value is a number, apply it to the other field. + if ( !isNaN( value ) ) + ( isWidth ? heightField : widthField ).setValue( value ); + } + + // Set-up function for lock and reset buttons: + // * Adds lock and reset buttons to focusables. Check if button exist first + // because it may be disabled e.g. due to ACF restrictions. + // * Register mouseover and mouseout event listeners for UI manipulations. + // * Register click event listeners for buttons. + function onLoadLockReset() { + var dialog = this.getDialog(); + + function setupMouseClasses( el ) { + el.on( 'mouseover', function() { + this.addClass( 'cke_btn_over' ); + }, el ); + + el.on( 'mouseout', function() { + this.removeClass( 'cke_btn_over' ); + }, el ); + } + + // Create references to lock and reset buttons for this dialog instance. + lockButton = doc.getById( lockButtonId ); + resetButton = doc.getById( resetButtonId ); + + // Activate (Un)LockRatio button + if ( lockButton ) { + // Consider that there's an additional focusable field + // in the dialog when the "browse" button is visible. + dialog.addFocusable( lockButton, 4 + hasFileBrowser ); + + lockButton.on( 'click', function( evt ) { + toggleLockRatio(); + evt.data && evt.data.preventDefault(); + }, this.getDialog() ); + + setupMouseClasses( lockButton ); + } + + // Activate the reset size button. + if ( resetButton ) { + // Consider that there's an additional focusable field + // in the dialog when the "browse" button is visible. + dialog.addFocusable( resetButton, 5 + hasFileBrowser ); + + // Fills width and height fields with the original dimensions of the + // image (stored in widget#data since widget#init). + resetButton.on( 'click', function( evt ) { + // If there's a new image loaded, reset button should revert + // cached dimensions of pre-loaded DOM element. + if ( srcChanged ) { + widthField.setValue( preLoadedWidth ); + heightField.setValue( preLoadedHeight ); + } + + // If the old image remains, reset button should revert + // dimensions as loaded when the dialog was first shown. + else { + widthField.setValue( domWidth ); + heightField.setValue( domHeight ); + } + + evt.data && evt.data.preventDefault(); + }, this ); + + setupMouseClasses( resetButton ); + } + } + + function toggleLockRatio( enable ) { + // No locking if there's no radio (i.e. due to ACF). + if ( !lockButton ) + return; + + if ( typeof enable == 'boolean' ) { + // If user explicitly wants to decide whether + // to lock or not, don't do anything. + if ( userDefinedLock ) + return; + + lockRatio = enable; + } + + // Undefined. User changed lock value. + else { + var width = widthField.getValue(), + height; + + userDefinedLock = true; + lockRatio = !lockRatio; + + // Automatically adjust height to width to match + // the original ratio (based on dom- dimensions). + if ( lockRatio && width ) { + height = domHeight / domWidth * width; + + if ( !isNaN( height ) ) + heightField.setValue( Math.round( height ) ); + } + } + + lockButton[ lockRatio ? 'removeClass' : 'addClass' ]( 'cke_btn_unlocked' ); + lockButton.setAttribute( 'aria-checked', lockRatio ); + + // Ratio button hc presentation - WHITE SQUARE / BLACK SQUARE + if ( CKEDITOR.env.hc ) { + var icon = lockButton.getChild( 0 ); + icon.setHtml( lockRatio ? CKEDITOR.env.ie ? '\u25A0' : '\u25A3' : CKEDITOR.env.ie ? '\u25A1' : '\u25A2' ); + } + } + + function toggleDimensions( enable ) { + var method = enable ? 'enable' : 'disable'; + + widthField[ method ](); + heightField[ method ](); + } + + var srcBoxChildren = [ + { + id: 'src', + type: 'text', + label: commonLang.url, + onKeyup: onChangeSrc, + onChange: onChangeSrc, + setup: function( widget ) { + this.setValue( widget.data.src ); + }, + commit: function( widget ) { + widget.setData( 'src', this.getValue() ); + }, + validate: CKEDITOR.dialog.validate.notEmpty( lang.urlMissing ) + } + ]; + + // Render the "Browse" button on demand to avoid an "empty" (hidden child) + // space in dialog layout that distorts the UI. + if ( hasFileBrowser ) { + srcBoxChildren.push( { + type: 'button', + id: 'browse', + // v-align with the 'txtUrl' field. + // TODO: We need something better than a fixed size here. + style: 'display:inline-block;margin-top:14px;', + align: 'center', + label: editor.lang.common.browseServer, + hidden: true, + filebrowser: 'info:src' + } ); + } + + return { + title: lang.title, + minWidth: 250, + minHeight: 100, + onLoad: function() { + // Create a "global" reference to the document for this dialog instance. + doc = this._.element.getDocument(); + + // Create a pre-loader used for determining dimensions of new images. + preLoader = createPreLoader(); + }, + onShow: function() { + // Create a "global" reference to edited widget. + widget = this.widget; + + // Create a "global" reference to widget's image. + image = widget.parts.image; + + // Reset global variables. + srcChanged = userDefinedLock = lockRatio = false; + + // Natural dimensions of the image. + natural = getNatural( image ); + + // Get the natural width of the image. + preLoadedWidth = domWidth = natural.width; + + // Get the natural height of the image. + preLoadedHeight = domHeight = natural.height; + }, + contents: [ + { + id: 'info', + label: lang.infoTab, + elements: [ + { + type: 'vbox', + padding: 0, + children: [ + { + type: 'hbox', + widths: [ '100%' ], + className: 'cke_dialog_image_url', + children: srcBoxChildren + } + ] + }, + { + id: 'alt', + type: 'text', + label: lang.alt, + setup: function( widget ) { + this.setValue( widget.data.alt ); + }, + commit: function( widget ) { + widget.setData( 'alt', this.getValue() ); + }, + validate: editor.config.image2_altRequired === true ? CKEDITOR.dialog.validate.notEmpty( lang.altMissing ) : null + }, + { + type: 'hbox', + widths: [ '25%', '25%', '50%' ], + requiredContent: features.dimension.requiredContent, + children: [ + { + type: 'text', + width: '45px', + id: 'width', + label: commonLang.width, + validate: validateDimension, + onKeyUp: onChangeDimension, + onLoad: function() { + widthField = this; + }, + setup: function( widget ) { + this.setValue( widget.data.width ); + }, + commit: function( widget ) { + widget.setData( 'width', this.getValue() ); + } + }, + { + type: 'text', + id: 'height', + width: '45px', + label: commonLang.height, + validate: validateDimension, + onKeyUp: onChangeDimension, + onLoad: function() { + heightField = this; + }, + setup: function( widget ) { + this.setValue( widget.data.height ); + }, + commit: function( widget ) { + widget.setData( 'height', this.getValue() ); + } + }, + { + id: 'lock', + type: 'html', + style: lockResetStyle, + onLoad: onLoadLockReset, + setup: function( widget ) { + toggleLockRatio( widget.data.lock ); + }, + commit: function( widget ) { + widget.setData( 'lock', lockRatio ); + }, + html: lockResetHtml + } + ] + }, + { + type: 'hbox', + id: 'alignment', + requiredContent: features.align.requiredContent, + children: [ + { + id: 'align', + type: 'radio', + items: [ + [ commonLang.alignNone, 'none' ], + [ commonLang.alignLeft, 'left' ], + [ commonLang.alignCenter, 'center' ], + [ commonLang.alignRight, 'right' ] + ], + label: commonLang.align, + setup: function( widget ) { + this.setValue( widget.data.align ); + }, + commit: function( widget ) { + widget.setData( 'align', this.getValue() ); + } + } + ] + }, + { + id: 'hasCaption', + type: 'checkbox', + label: lang.captioned, + requiredContent: features.caption.requiredContent, + setup: function( widget ) { + this.setValue( widget.data.hasCaption ); + }, + commit: function( widget ) { + widget.setData( 'hasCaption', this.getValue() ); + } + } + ] + }, + { + id: 'Upload', + hidden: true, + filebrowser: 'uploadButton', + label: lang.uploadTab, + elements: [ + { + type: 'file', + id: 'upload', + label: lang.btnUpload, + style: 'height:40px' + }, + { + type: 'fileButton', + id: 'uploadButton', + filebrowser: 'info:src', + label: lang.btnUpload, + 'for': [ 'Upload', 'upload' ] + } + ] + } + ] + }; +} ); -- cgit 1.4.1