123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581 |
- /**
- * @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_chamilo', 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_chamilo,
- commonLang = editor.lang.common,
- lockResetStyle = 'margin-top:18px;width:40px;height:20px;',
- lockResetHtml = new CKEDITOR.template(
- '<div>' +
- '<a href="javascript:void(0)" tabindex="-1" title="' + lang.lockRatio + '" class="cke_btn_locked" id="{lockButtonId}" role="checkbox">' +
- '<span class="cke_icon"></span>' +
- '<span class="cke_label">' + lang.lockRatio + '</span>' +
- '</a>' +
- '<a href="javascript:void(0)" tabindex="-1" title="' + lang.resetSize + '" class="cke_btn_reset" id="{resetButtonId}" role="button">' +
- '<span class="cke_label">' + lang.resetSize + '</span>' +
- '</a>' +
- '</div>' ).output( {
- lockButtonId: lockButtonId,
- resetButtonId: resetButtonId
- } ),
- helpers = CKEDITOR.plugins.image2_chamilo,
- // 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_chamilo 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 (#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_chamilo_prefillDimensions === false ? 0 : width );
- // Fill height field with the height of the new image.
- heightField.setValue( editor.config.image2_chamilo_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_chamilo_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: 'select',
- items: [
- [ commonLang.alignNone, 'none' ],
- [ commonLang.alignLeft, 'left' ],
- [ commonLang.alignCenter, 'center' ],
- [ commonLang.alignRight, 'right' ],
- [ lang.alignBaseline, 'baseline'],
- [ lang.alignTop, 'top'],
- [ lang.alignBottom, 'bottom'],
- [ lang.alignMiddle, 'middle'],
- [ lang.alignSuper, 'super'],
- [ lang.alignSub, 'sub'],
- [ lang.alignTextTop, 'text-top'],
- [ lang.alignTextBottom, 'text-bottom'],
- ],
- 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: 'isResponsive',
- type: 'checkbox',
- label: lang.responsive,
- requiredContent: features.responsive.requiredContent,
- setup: function ( widget ) {
- this.setValue( widget.data.isResponsive );
- },
- commit: function ( widget ) {
- var img = widget;
- if (widget.element.$.tagName === 'FIGURE') {
- img = widget.element.$.firstChild;
- }
- img.className += ' img-responsive ';
- widget.setData( 'isResponsive', 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' ]
- }
- ]
- }
- ]
- };
- } );
|