Annotation of loncom/html/adm/ckeditor/plugins/lcm/plugin.js, revision 1.1

1.1     ! damieng     1: /**
        !             2:  * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
        !             3:  * For licensing, see LICENSE.md or http://ckeditor.com/license
        !             4:  */
        !             5: 
        !             6: /**
        !             7:  * @fileOverview [Mathematical Formulas] LON-CAPA ckeditor plugin for the m element, derived from ckeditor mathjax plugin.
        !             8:  */
        !             9: 
        !            10: 'use strict';
        !            11: 
        !            12: ( function() {
        !            13: 
        !            14:     var cdn = 'http:\/\/cdn.mathjax.org\/mathjax\/2.2-latest\/MathJax.js?config=TeX-AMS_HTML';
        !            15: 
        !            16:     CKEDITOR.plugins.add( 'lcm', {
        !            17:         lang: 'af,ar,ca,cs,cy,da,de,el,en,en-gb,eo,es,fa,fi,fr,gl,he,hr,hu,it,ja,km,ku,lt,nb,nl,no,pl,pt,pt-br,ro,ru,sk,sl,sq,sv,tr,tt,uk,vi,zh,zh-cn', // %REMOVE_LINE_CORE%
        !            18:         requires: 'widget,dialog',
        !            19:         icons: 'lcm',
        !            20:         hidpi: true, // %REMOVE_LINE_CORE%
        !            21: 
        !            22:         init: function( editor ) {
        !            23:             var cls = editor.config.mathJaxClass || 'math-tex';
        !            24: 
        !            25:             editor.widgets.add( 'lcm', {
        !            26:                 inline: true,
        !            27:                 dialog: 'lcm',
        !            28:                 button: editor.lang.lcm.button,
        !            29:                 mask: true,
        !            30:                 allowedContent: 'span(!' + cls + ')',
        !            31:                 // Allow style classes only on spans having lcm class.
        !            32:                 styleToAllowedContentRules: function( style ) {
        !            33:                     var classes = style.getClassesArray();
        !            34:                     if ( !classes )
        !            35:                         return null;
        !            36:                     classes.push( '!' + cls );
        !            37: 
        !            38:                     return 'span(' + classes.join( ',' ) + ')';
        !            39:                 },
        !            40:                 pathName: editor.lang.lcm.pathName,
        !            41: 
        !            42:                 template: '<span class="' + cls + '" style="display:inline-block" data-cke-survive=1></span>',
        !            43: 
        !            44:                 parts: {
        !            45:                     span: 'span'
        !            46:                 },
        !            47: 
        !            48:                 defaults: {
        !            49:                     math: '$ x $' // the default helps to show the $...$ syntax
        !            50:                 },
        !            51: 
        !            52:                 init: function() {
        !            53:                     var iframe = this.parts.span.getChild( 0 );
        !            54: 
        !            55:                     // Check if span contains iframe and create it otherwise.
        !            56:                     if ( !iframe || iframe.type != CKEDITOR.NODE_ELEMENT || !iframe.is( 'iframe' ) ) {
        !            57:                         iframe = new CKEDITOR.dom.element( 'iframe' );
        !            58:                         iframe.setAttributes( {
        !            59:                             style: 'border:0;width:0;height:0',
        !            60:                             scrolling: 'no',
        !            61:                             frameborder: 0,
        !            62:                             allowTransparency: true,
        !            63:                             src: CKEDITOR.plugins.lcm.fixSrc
        !            64:                         } );
        !            65:                         this.parts.span.append( iframe );
        !            66:                     }
        !            67: 
        !            68:                     // Wait for ready because on some browsers iFrame will not
        !            69:                     // have document element until it is put into document.
        !            70:                     // This is a problem when you crate widget using dialog.
        !            71:                     this.once( 'ready', function() {
        !            72:                         // Src attribute must be recreated to fix custom domain error after undo
        !            73:                         // (see iFrame.removeAttribute( 'src' ) in frameWrapper.load).
        !            74:                         if ( CKEDITOR.env.ie )
        !            75:                             iframe.setAttribute( 'src', CKEDITOR.plugins.lcm.fixSrc );
        !            76: 
        !            77:                         this.frameWrapper = new CKEDITOR.plugins.lcm.frameWrapper( iframe, editor );
        !            78:                         this.frameWrapper.setValue( this.data.math );
        !            79:                     } );
        !            80:                 },
        !            81: 
        !            82:                 data: function() {
        !            83:                     if ( this.frameWrapper )
        !            84:                         this.frameWrapper.setValue( this.data.math );
        !            85:                 },
        !            86: 
        !            87:                 upcast: function( el, data ) {
        !            88:                     if ( !( el.name == 'm' ) )
        !            89:                         return;
        !            90: 
        !            91:                     if ( el.children.length > 1 || el.children[ 0 ].type != CKEDITOR.NODE_TEXT )
        !            92:                         return;
        !            93: 
        !            94:                     data.math = CKEDITOR.tools.htmlDecode( el.children[ 0 ].value );
        !            95:                     
        !            96:                     // NOTE: we do not preserve the display attribute, because only MathJax is used for preview
        !            97:                     var span = new CKEDITOR.htmlParser.element('span');
        !            98:                     span.addClass(cls);
        !            99:                     
        !           100:                     // Add style display:inline-block to have proper height of widget wrapper and mask.
        !           101:                     var attrs = span.attributes;
        !           102: 
        !           103:                     if ( attrs.style )
        !           104:                         attrs.style += ';display:inline-block';
        !           105:                     else
        !           106:                         attrs.style = 'display:inline-block';
        !           107: 
        !           108:                     // Add attribute to prevent deleting empty span in data processing.
        !           109:                     attrs[ 'data-cke-survive' ] = 1;
        !           110:                     if (el.attributes.eval)
        !           111:                         attrs['data-eval'] = el.attributes.eval;
        !           112:                     // NOTE: the display attribute is not preserved
        !           113:                     el.replaceWith(span);
        !           114:                     return span;
        !           115:                 },
        !           116: 
        !           117:                 downcast: function( el ) {
        !           118:                     var m = new CKEDITOR.htmlParser.element('m');
        !           119:                     if (el.attributes['data-eval'])
        !           120:                         m.attributes.eval = el.attributes['data-eval'];
        !           121:                     m.add(new CKEDITOR.htmlParser.text(CKEDITOR.tools.htmlEncode(this.data.math)));
        !           122:                     return m;
        !           123:                 }
        !           124:             } );
        !           125: 
        !           126:             // Add dialog.
        !           127:             CKEDITOR.dialog.add( 'lcm', this.path + 'dialogs/lcm.js' );
        !           128: 
        !           129:             // Add MathJax script to page preview.
        !           130:             editor.on( 'contentPreview', function( evt ) {
        !           131:                 evt.data.dataValue = evt.data.dataValue.replace( /<\/head>/,
        !           132:                     '<script src="' + ( editor.config.mathJaxLib ? CKEDITOR.getUrl( editor.config.mathJaxLib ) : cdn ) + '"><\/script><\/head>' );
        !           133:             } );
        !           134: 
        !           135:             editor.on( 'paste', function( evt ) {
        !           136:                 // Firefox does remove iFrame elements from pasted content so this event do the same on other browsers.
        !           137:                 // Also iFrame in paste content is reason of "Unspecified error" in IE9 (#10857).
        !           138:                 var regex = new RegExp( '<span[^>]*?' + cls + '.*?<\/span>', 'ig' );
        !           139:                 evt.data.dataValue = evt.data.dataValue.replace( regex, function( match ) {
        !           140:                     return match.replace( /(<iframe.*?\/iframe>)/i, '' );
        !           141:                 } );
        !           142:             } );
        !           143:         }
        !           144:     } );
        !           145: 
        !           146:     /**
        !           147:      * @private
        !           148:      * @singleton
        !           149:      * @class CKEDITOR.plugins.lcm
        !           150:      */
        !           151:     CKEDITOR.plugins.lcm = {};
        !           152: 
        !           153:     /**
        !           154:      * A variable to fix problems with `iframe`. This variable is global
        !           155:      * because it is used in both the widget and the dialog window.
        !           156:      *
        !           157:      * @private
        !           158:      * @property {String} fixSrc
        !           159:      */
        !           160:     CKEDITOR.plugins.lcm.fixSrc =
        !           161:         // In Firefox src must exist and be different than about:blank to emit load event.
        !           162:         CKEDITOR.env.gecko ? 'javascript:true' : // jshint ignore:line
        !           163:         // Support for custom document.domain in IE.
        !           164:         CKEDITOR.env.ie ? 'javascript:' + // jshint ignore:line
        !           165:                         'void((function(){' + encodeURIComponent(
        !           166:                             'document.open();' +
        !           167:                             '(' + CKEDITOR.tools.fixDomain + ')();' +
        !           168:                             'document.close();'
        !           169:                         ) + '})())' :
        !           170:         // In Chrome src must be undefined to emit load event.
        !           171:                         'javascript:void(0)'; // jshint ignore:line
        !           172: 
        !           173:     /**
        !           174:      * Loading indicator image generated by http://preloaders.net.
        !           175:      *
        !           176:      * @private
        !           177:      * @property {String} loadingIcon
        !           178:      */
        !           179:     CKEDITOR.plugins.lcm.loadingIcon = CKEDITOR.plugins.get( 'lcm' ).path + 'images/loader.gif';
        !           180: 
        !           181:     /**
        !           182:      * Computes predefined styles and copies them to another element.
        !           183:      *
        !           184:      * @private
        !           185:      * @param {CKEDITOR.dom.element} from Copy source.
        !           186:      * @param {CKEDITOR.dom.element} to Copy target.
        !           187:      */
        !           188:     CKEDITOR.plugins.lcm.copyStyles = function( from, to ) {
        !           189:         var stylesToCopy = [ 'color', 'font-family', 'font-style', 'font-weight', 'font-variant', 'font-size' ];
        !           190: 
        !           191:         for ( var i = 0; i < stylesToCopy.length; i++ ) {
        !           192:             var key = stylesToCopy[ i ],
        !           193:                 val = from.getComputedStyle( key );
        !           194:             if ( val )
        !           195:                 to.setStyle( key, val );
        !           196:         }
        !           197:     };
        !           198: 
        !           199:     /**
        !           200:      * Trims MathJax value from '\(1+1=2\)' to '1+1=2'.
        !           201:      *
        !           202:      * @private
        !           203:      * @param {String} value String to trim.
        !           204:      * @returns {String} Trimed string.
        !           205:      */
        !           206:     CKEDITOR.plugins.lcm.trim = function( value ) {
        !           207:         var begin = value.indexOf( '\\(' ) + 2,
        !           208:             end = value.lastIndexOf( '\\)' );
        !           209: 
        !           210:         return value.substring( begin, end );
        !           211:     };
        !           212: 
        !           213:     /**
        !           214:      * FrameWrapper is responsible for communication between the MathJax library
        !           215:      * and the `iframe` element that is used for rendering mathematical formulas
        !           216:      * inside the editor.
        !           217:      * It lets you create visual mathematics by using the
        !           218:      * {@link CKEDITOR.plugins.lcm.frameWrapper#setValue setValue} method.
        !           219:      *
        !           220:      * @private
        !           221:      * @class CKEDITOR.plugins.lcm.frameWrapper
        !           222:      * @constructor Creates a class instance.
        !           223:      * @param {CKEDITOR.dom.element} iFrame The `iframe` element to be wrapped.
        !           224:      * @param {CKEDITOR.editor} editor The editor instance.
        !           225:      */
        !           226:     if ( !( CKEDITOR.env.ie && CKEDITOR.env.version == 8 ) ) {
        !           227:         CKEDITOR.plugins.lcm.frameWrapper = function( iFrame, editor ) {
        !           228: 
        !           229:             var buffer, preview, value, newValue,
        !           230:                 doc = iFrame.getFrameDocument(),
        !           231: 
        !           232:                 // Is MathJax loaded and ready to work.
        !           233:                 isInit = false,
        !           234: 
        !           235:                 // Is MathJax parsing Tex.
        !           236:                 isRunning = false,
        !           237: 
        !           238:                 // Function called when MathJax is loaded.
        !           239:                 loadedHandler = CKEDITOR.tools.addFunction( function() {
        !           240:                     preview = doc.getById( 'preview' );
        !           241:                     buffer = doc.getById( 'buffer' );
        !           242:                     isInit = true;
        !           243: 
        !           244:                     if ( newValue )
        !           245:                         update();
        !           246: 
        !           247:                     // Private! For test usage only.
        !           248:                     CKEDITOR.fire( 'mathJaxLoaded', iFrame );
        !           249:                 } ),
        !           250: 
        !           251:                 // Function called when MathJax finish his job.
        !           252:                 updateDoneHandler = CKEDITOR.tools.addFunction( function() {
        !           253:                     CKEDITOR.plugins.lcm.copyStyles( iFrame, preview );
        !           254: 
        !           255:                     preview.setHtml( buffer.getHtml() );
        !           256: 
        !           257:                     editor.fire( 'lockSnapshot' );
        !           258: 
        !           259:                     iFrame.setStyles( {
        !           260:                         height: 0,
        !           261:                         width: 0
        !           262:                     } );
        !           263: 
        !           264:                     // Set iFrame dimensions.
        !           265:                     var height = Math.max( doc.$.body.offsetHeight, doc.$.documentElement.offsetHeight ),
        !           266:                         width = Math.max( preview.$.offsetWidth, doc.$.body.scrollWidth );
        !           267: 
        !           268:                     iFrame.setStyles( {
        !           269:                         height: height + 'px',
        !           270:                         width: width + 'px'
        !           271:                     } );
        !           272: 
        !           273:                     editor.fire( 'unlockSnapshot' );
        !           274: 
        !           275:                     // Private! For test usage only.
        !           276:                     CKEDITOR.fire( 'mathJaxUpdateDone', iFrame );
        !           277: 
        !           278:                     // If value changed in the meantime update it again.
        !           279:                     if ( value != newValue )
        !           280:                         update();
        !           281:                     else
        !           282:                         isRunning = false;
        !           283:                 } );
        !           284: 
        !           285:             iFrame.on( 'load', load );
        !           286: 
        !           287:             load();
        !           288: 
        !           289:             function load() {
        !           290:                 doc = iFrame.getFrameDocument();
        !           291: 
        !           292:                 if ( doc.getById( 'preview' ) )
        !           293:                     return;
        !           294: 
        !           295:                 // Because of IE9 bug in a src attribute can not be javascript
        !           296:                 // when you undo (#10930). If you have iFrame with javascript in src
        !           297:                 // and call insertBefore on such element then IE9 will see crash.
        !           298:                 if ( CKEDITOR.env.ie )
        !           299:                     iFrame.removeAttribute( 'src' );
        !           300: 
        !           301:                 doc.write( '<!DOCTYPE html>' +
        !           302:                             '<html>' +
        !           303:                             '<head>' +
        !           304:                                 '<meta charset="utf-8">' +
        !           305:                                 '<script type="text/x-mathjax-config">' +
        !           306: 
        !           307:                                     // MathJax configuration, disable messages.
        !           308:                                     'MathJax.Hub.Config( {' +
        !           309:                                         'showMathMenu: false,' +
        !           310:                                         'messageStyle: "none"' +
        !           311:                                     '} );' +
        !           312: 
        !           313:                                     // Get main CKEDITOR form parent.
        !           314:                                     'function getCKE() {' +
        !           315:                                         'if ( typeof window.parent.CKEDITOR == \'object\' ) {' +
        !           316:                                             'return window.parent.CKEDITOR;' +
        !           317:                                         '} else {' +
        !           318:                                             'return window.parent.parent.CKEDITOR;' +
        !           319:                                         '}' +
        !           320:                                     '}' +
        !           321: 
        !           322:                                     // Run MathJax.Hub with its actual parser and call callback function after that.
        !           323:                                     // Because MathJax.Hub is asynchronous create MathJax.Hub.Queue to wait with callback.
        !           324:                                     'function update() {' +
        !           325:                                         'MathJax.Hub.Queue(' +
        !           326:                                             '[ \'Typeset\', MathJax.Hub, this.buffer ],' +
        !           327:                                             'function() {' +
        !           328:                                                 'getCKE().tools.callFunction( ' + updateDoneHandler + ' );' +
        !           329:                                             '}' +
        !           330:                                         ');' +
        !           331:                                     '}' +
        !           332: 
        !           333:                                     // Run MathJax for the first time, when the script is loaded.
        !           334:                                     // Callback function will be called then it's done.
        !           335:                                     'MathJax.Hub.Queue( function() {' +
        !           336:                                         'getCKE().tools.callFunction(' + loadedHandler + ');' +
        !           337:                                     '} );' +
        !           338:                                 '</script>' +
        !           339: 
        !           340:                                 // Load MathJax lib.
        !           341:                                 '<script src="' + ( editor.config.mathJaxLib || cdn ) + '"></script>' +
        !           342:                             '</head>' +
        !           343:                             '<body style="padding:0;margin:0;background:transparent;overflow:hidden">' +
        !           344:                                 '<span id="preview"></span>' +
        !           345: 
        !           346:                                 // Render everything here and after that copy it to the preview.
        !           347:                                 '<span id="buffer" style="display:none"></span>' +
        !           348:                             '</body>' +
        !           349:                             '</html>' );
        !           350:             }
        !           351: 
        !           352:             // Run MathJax parsing Tex.
        !           353:             function update() {
        !           354:                 isRunning = true;
        !           355: 
        !           356:                 value = newValue;
        !           357: 
        !           358:                 editor.fire( 'lockSnapshot' );
        !           359: 
        !           360:                 buffer.setHtml( value );
        !           361: 
        !           362:                 // Set loading indicator.
        !           363:                 preview.setHtml( '<img src=' + CKEDITOR.plugins.lcm.loadingIcon + ' alt=' + editor.lang.lcm.loading + '>' );
        !           364: 
        !           365:                 iFrame.setStyles( {
        !           366:                     height: '16px',
        !           367:                     width: '16px',
        !           368:                     display: 'inline',
        !           369:                     'vertical-align': 'middle'
        !           370:                 } );
        !           371: 
        !           372:                 editor.fire( 'unlockSnapshot' );
        !           373: 
        !           374:                 // Run MathJax.
        !           375:                 doc.getWindow().$.update( value );
        !           376:             }
        !           377: 
        !           378:             return {
        !           379:                 /**
        !           380:                  * Sets the TeX value to be displayed in the `iframe` element inside
        !           381:                  * the editor. This function will activate the MathJax
        !           382:                  * library which interprets TeX expressions and converts them into
        !           383:                  * their representation that is displayed in the editor.
        !           384:                  *
        !           385:                  * @param {String} value TeX string.
        !           386:                  */
        !           387:                 setValue: function( value ) {
        !           388:                     if (/^\s*\$\$.*\$\$\s*$/.test(value)) {
        !           389:                         // replace $$...$$ by \[...\]
        !           390:                         value = value.replace(/^\s*\$\$(.*)\$\$\s*$/, '\\[$1\\]');
        !           391:                     } else if (/^\s*\$.*\$\s*$/.test(value)) {
        !           392:                         // replace $...$ by \(...\)
        !           393:                         value = value.replace(/^\s*\$(.*)\$\s*$/, '\\($1\\)');
        !           394:                     } else if (/^\s*\\\(.*\\\)\s*$/.test(value)) {
        !           395:                         // leave \( \) as is
        !           396:                     } else if (/^\s*\\\[.*\\\]\s*$/.test(value)) {
        !           397:                         // leave \[ \] as is
        !           398:                     } else {
        !           399:                         // add \( \)
        !           400:                         value = '\\(' + value + '\\)';
        !           401:                     }
        !           402:                     newValue = CKEDITOR.tools.htmlEncode( value );
        !           403: 
        !           404:                     if ( isInit && !isRunning )
        !           405:                         update();
        !           406:                 }
        !           407:             };
        !           408:         };
        !           409:     } else {
        !           410:         // In IE8 MathJax does not work stable so instead of using standard
        !           411:         // frame wrapper it is replaced by placeholder to show pure TeX in iframe.
        !           412:         CKEDITOR.plugins.lcm.frameWrapper = function( iFrame, editor ) {
        !           413:             iFrame.getFrameDocument().write( '<!DOCTYPE html>' +
        !           414:                 '<html>' +
        !           415:                 '<head>' +
        !           416:                     '<meta charset="utf-8">' +
        !           417:                 '</head>' +
        !           418:                 '<body style="padding:0;margin:0;background:transparent;overflow:hidden">' +
        !           419:                     '<span style="white-space:nowrap;" id="tex"></span>' +
        !           420:                 '</body>' +
        !           421:                 '</html>' );
        !           422: 
        !           423:             return {
        !           424:                 setValue: function( value ) {
        !           425:                     var doc = iFrame.getFrameDocument(),
        !           426:                         tex = doc.getById( 'tex' );
        !           427: 
        !           428:                     tex.setHtml( CKEDITOR.plugins.lcm.trim( CKEDITOR.tools.htmlEncode( value ) ) );
        !           429: 
        !           430:                     CKEDITOR.plugins.lcm.copyStyles( iFrame, tex );
        !           431: 
        !           432:                     editor.fire( 'lockSnapshot' );
        !           433: 
        !           434:                     iFrame.setStyles( {
        !           435:                         width: Math.min( 250, tex.$.offsetWidth ) + 'px',
        !           436:                         height: doc.$.body.offsetHeight + 'px',
        !           437:                         display: 'inline',
        !           438:                         'vertical-align': 'middle'
        !           439:                     } );
        !           440: 
        !           441:                     editor.fire( 'unlockSnapshot' );
        !           442:                 }
        !           443:             };
        !           444:         };
        !           445:     }
        !           446: } )();
        !           447: 
        !           448: /**
        !           449:  * Sets the path to the MathJax library. It can be both a local
        !           450:  * resource and a location different than the default CDN.
        !           451:  *
        !           452:  * Please note that this must be a full or absolute path.
        !           453:  *
        !           454:  *        config.mathJaxLib = 'http:\/\/example.com\/libs\/MathJax.js';
        !           455:  *
        !           456:  * @cfg {String} [mathJaxLib='http:\/\/cdn.mathjax.org\/mathjax\/2.2-latest\/MathJax.js?config=TeX-AMS_HTML']
        !           457:  * @member CKEDITOR.config
        !           458:  */
        !           459: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>