Ticket #5317: 5317_1.patch

File 5317_1.patch, 21.7 KB (added by Alfonso Martínez de Lizarrondo, 9 years ago)

Little clean up

  • _dev/leakDetector/detector.js

    Property changes on: _dev\leakDetector
    ___________________________________________________________________
    Added: bugtraq:label
       + Ticket
    Added: bugtraq:url
       + http://dev.fckeditor.net/ticket/%BUGID%
    Added: webviewer:pathrevision
       + http://dev.fckeditor.net/browser/%PATH%?rev=%REVISION% 
    Added: webviewer:revision
       + http://dev.fckeditor.net/changeset/%REVISION%
    Added: bugtraq:logregex
       + (?:ticket: *|#)(\d+) *(?:, *(\d+))*
    
    
     
     1
     2var garbageDetector = {
     3
     4        getStackTrace: function()
     5        {
     6                if ( !garbageDetector.getStack )
     7                        return [''];
     8
     9                // Skip the first two calls (instrumentation) and get only 5 levels deep
     10                return printStackTrace().slice(2, 7);
     11        },
     12
     13        // Hook on special CKEDITOR methods
     14        init: function()
     15        {
     16                //
     17                this.getStack = false;
     18
     19                // tools.addFunction et.al
     20                // We store in this object every function that it's passed to tools.addFunction
     21                // And then check if it's properly removed.
     22                this.functionsStackTraces = {};
     23                this.DOMObjects = {};
     24                CKEDITOR.tools.addFunction = CKEDITOR.tools.override( CKEDITOR.tools.addFunction, function( addOriginal )
     25                        {
     26                                        return function( fn, scope )
     27                                                        {
     28                                                                        var index = addOriginal.apply( this, arguments );
     29                                                                        garbageDetector.functionsStackTraces[ index ] = garbageDetector.getStackTrace();
     30                                                                        return index;
     31                                                        };
     32                        });
     33
     34                CKEDITOR.tools.removeFunction = CKEDITOR.tools.override( CKEDITOR.tools.removeFunction, function( removeOriginal )
     35                        {
     36                                        return function( ref )
     37                                                        {
     38                                                                        removeOriginal.apply( this, arguments );
     39                                                                        delete garbageDetector.functionsStackTraces[ ref ];
     40                                                        };
     41                        });
     42
     43        // CKEDITOR.dom.domObject listeners
     44                CKEDITOR.dom.domObject.prototype.on = CKEDITOR.tools.override( CKEDITOR.dom.domObject.prototype.on, function( onOriginal )
     45                        {
     46                                        return function()
     47                                                        {
     48                                                                        var value = onOriginal.apply( this, arguments );
     49                                                                        garbageDetector.storeUsedObject( this, garbageDetector.getStackTrace() );
     50                                                                        return value;
     51                                                        };
     52                        });
     53
     54                CKEDITOR.dom.domObject.prototype.removeAllListeners = CKEDITOR.tools.override( CKEDITOR.dom.domObject.prototype.removeAllListeners, function( removeOriginal )
     55                        {
     56                                        return function()
     57                                                        {
     58                                                                        garbageDetector.clearUsedObject( this );
     59                                                                        removeOriginal.apply( this );
     60                                                        };
     61                        });
     62
     63        },
     64
     65        // Track usage and clean up of domObject elements
     66        storeUsedObject : function( obj, stack )
     67        {
     68                // Warning: any object might get multiple uses, but we only store one callstack
     69                garbageDetector.DOMObjects[ obj.$._cke_expando ] = { obj : obj, stack : stack };
     70        },
     71        clearUsedObject : function( obj )
     72        {
     73                delete garbageDetector.DOMObjects[ obj.$._cke_expando ];
     74        },
     75
     76        // Take a snapshot of the initial object
     77        // We are interested in what has been modified during the test, not after it.
     78        startLeaksCheck : function()
     79        {
     80                this.getStack = true;
     81
     82                // Reset previous data
     83                garbageDetector.DOMObjects = {};
     84
     85                var reference = {};
     86                reference.functions = CKEDITOR.tools.clone( this.functionsStackTraces );
     87                reference.events = CKEDITOR.tools.clone( CKEDITOR._.events );
     88                reference.CKEDITOR = this.safeDeepClone( CKEDITOR, 'CKEDITOR' );
     89                this.reference = reference;
     90        },
     91
     92
     93        // Copied from CKEDITOR.tools.clone, but will skip any $ as they can't be cloned
     94        safeDeepClone : function( obj, name )
     95        {
     96                var clone;
     97
     98                // Array.
     99                if ( obj && ( obj instanceof Array ) )
     100                {
     101                        clone = [];
     102
     103                        for ( var i = 0 ; i < obj.length ; i++ )
     104                                clone[ i ] = this.safeDeepClone( obj[ i ], name + '[' + i + ']' );
     105
     106                        return clone;
     107                }
     108
     109                // "Static" types.
     110                if ( obj === null
     111                        || ( typeof obj == 'string' )
     112                        || ( obj instanceof String )
     113                        || ( obj instanceof Number )
     114                        || ( obj instanceof Boolean )
     115                        || ( obj instanceof Date )
     116                        || ( obj instanceof RegExp) )
     117                {
     118                        return obj;
     119                }
     120
     121                // Objects.
     122                clone = {};
     123
     124                for ( var propertyName in obj )
     125                {
     126                        var property = obj[ propertyName ];
     127                        // avoid infinite loops
     128                        if ( propertyName == 'next' || propertyName == 'previous' )
     129                        {
     130                                continue;
     131                        }
     132                        // Special protection for the detector.
     133                        if ( propertyName == '$' || propertyName == 'prototype' )
     134                        {
     135                                clone[ propertyName ] = {};// something to note that it existed
     136                                continue;
     137                        }
     138                        if ( propertyName == 'editor' && name != 'CKEDITOR' )
     139                        {
     140                                clone[ propertyName ] = {};// something to note that it existed
     141                                continue;
     142                        }
     143                        if ( propertyName == 'toolbar' && name != 'CKEDITOR.plugins.registered' )
     144                        {
     145                                clone[ propertyName ] = {};// something to note that it existed
     146                                continue;
     147                        }
     148
     149                        clone[ propertyName ] = this.safeDeepClone( property , name + '.' + propertyName );
     150                }
     151
     152                return clone;
     153        },
     154
     155
     156        endLeaksCheck : function()
     157        {
     158                this.getStack = false;
     159                var messages = [];
     160
     161                // Check for created functions:
     162                var oldFunctions = this.reference.functions;
     163                var newFunctionsData = this.functionsStackTraces ;
     164                for ( var i in newFunctionsData )
     165                {
     166                        if ( !oldFunctions[ i ] )
     167                        {
     168                                messages.push( '<h2>Leaked function ' + i + '</h2>' );
     169                                messages.push( this.printStack( newFunctionsData[ i ] ) );
     170                        }
     171                }
     172
     173                // Check for global events
     174                var oldEvents = this.reference.events;
     175                var newEvents = CKEDITOR._.events ;
     176
     177                for ( var e in  newEvents )
     178                {
     179                        var data = newEvents[ e ];
     180                        var oldData = oldEvents[ e ];
     181                        if (!oldData)
     182                        {
     183                                messages.push( '<h2>New event handler in CKEDITOR for ' + e + ' event</h2>' );
     184                        }
     185                        else
     186                        {
     187                                if (data.listeners.length > oldData.listeners.length)
     188                                {
     189                                        messages.push( '<h2>New listener in CKEDITOR for ' + e + ' event</h2>');
     190                                }
     191                        }
     192                }
     193
     194                for ( o in this.DOMObjects )
     195                {
     196                        var names = this.getListenerNames( this.DOMObjects[ o ].obj );
     197//                      if ( names.length > 0 )
     198                        {
     199                                // The object might have been added, but the listeners cleaned up.
     200                                messages.push( '<h2>Listeners not cleaned up on ' + this.outputObject( this.DOMObjects[ o ].obj.$ ) + '</h2>' );
     201                                messages.push( '<p>Events hooked:<br>' + names.join( '<br>' ) );
     202                                messages.push( this.printStack( this.DOMObjects[ o ].stack ) + '</p>' );
     203                                if (window.console) console.log( this.DOMObjects[ o ].obj );
     204                        }
     205                }
     206
     207                // Any other property set on the global CKEDITOR
     208                messages.push( this.compareObject( this.reference.CKEDITOR, CKEDITOR, 'CKEDITOR' ) );
     209
     210                messages.push( '<h2>End of report</h2>' );
     211                return messages.join( '\r\n' );
     212        },
     213
     214        compareObject : function( reference, current, title)
     215        {
     216/*
     217                console.log('checking ' + title);
     218                console.log(typeof current);
     219                console.log( current );
     220*/
     221                var currentType = (typeof current);
     222                if ( currentType == 'string' ) return '';
     223
     224
     225                var messages = [];
     226                for( var o in current )
     227                {
     228                        // Don't check for native objects
     229                        if ( o == '$' )
     230                                continue;
     231
     232                        // Causes infinite recursion
     233                        if ( o == 'prototype' )
     234                                continue;
     235
     236                        if ( !(o in reference) )
     237                                messages.push( '<h2>New element "' + title + '.' + o + '"</h2>' );
     238                        else
     239                        {
     240                                var obj = current[ o ],
     241                                        old = reference[ o ];
     242
     243                                if ( !obj )
     244                                        continue;
     245
     246                                if ( obj instanceof Array )
     247                                {
     248                                        for ( var i = old.length ; i < obj.length ; i++ )
     249                                        {
     250                                                if ( obj[ i ] )
     251                                                        messages.push( '<h2>New contents in array ' + title + '.' + o + '[' + i + ']</h2>' );
     252                                        }
     253                                        continue;
     254                                }
     255
     256                                // "Static" types.
     257                                if ( obj === null
     258                                        || ( obj instanceof String )
     259                                        || ( obj instanceof Number )
     260                                        || ( obj instanceof Boolean )
     261                                        || ( obj instanceof Date )
     262                                        || ( obj instanceof RegExp) )
     263                                {
     264                                        continue;
     265                                }
     266
     267                                messages.push( this.compareObject( old, obj, title + '.' + o) );
     268                        }
     269                }
     270                return messages.join( '\r\n' );
     271        },
     272
     273        getListenerNames : function( obj )
     274        {
     275                var names = [];
     276                var nativeListeners = obj.getCustomData( '_cke_nativeListeners' );
     277                for ( eventName in nativeListeners)
     278                {
     279                        names.push( eventName );
     280                }
     281                return names;
     282        },
     283
     284        outputObject : function( obj )
     285        {
     286                var text = '&lt;' + obj.nodeName;
     287                text += '  _cke_expando="' + obj. _cke_expando + '"';
     288                if (obj.id) text += ' id="' + obj.id + '"';
     289                if (obj.className) text += ' class="' + obj.className + '"';
     290                text += '&gt;';
     291                return '<span class="object">' + text + '</span>';
     292        },
     293
     294        printStack : function( data )
     295        {
     296                var text = data.join( '\r\n' );
     297                text = text.replace( /</g , '&lt;' ).replace( />/g , '&gt;' );
     298                return '<p class="stack">' + text + '</p>';
     299        }
     300
     301};
     302 No newline at end of file
  • _dev/leakDetector/leakDetector.html

     
     1<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
     2<!--
     3Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
     4For licensing, see LICENSE.html or http://ckeditor.com/license
     5-->
     6<html xmlns="http://www.w3.org/1999/xhtml">
     7<head>
     8        <title>Leak Detector - CKEditor Sample</title>
     9        <meta content="text/html; charset=utf-8" http-equiv="content-type" />
     10        <script type="text/javascript" src="../../ckeditor_source.js"></script>
     11        <script type="text/javascript" src="stacktrace.js"></script>
     12        <script type="text/javascript" src="detector.js"></script>
     13        <script type="text/javascript">
     14        //<![CDATA[
     15var editor;
     16var allPlugins = 'about,a11yhelp,basicstyles,blockquote,button,clipboard,colorbutton,colordialog,contextmenu,div,elementspath,enterkey,entities,filebrowser,find,flash,font,format,forms,horizontalrule,htmldataprocessor,image,indent,justify,keystrokes,link,list,maximize,newpage,pagebreak,pastefromword,pastetext,popup,preview,print,removeformat,resize,save,scayt,smiley,showblocks,showborders,sourcearea,stylescombo,table,tabletools,specialchar,tab,templates,toolbar,undo,wysiwygarea,wsc';
     17
     18var removePlugins = '';
     19
     20//      toolbarCanCollapse : false,
     21//      startupMode : 'source',
     22//      extraPlugins: 'selection',
     23
     24var config = {
     25        removePlugins : removePlugins,
     26        customConfig : ''
     27}
     28
     29function createEditor()
     30{
     31        if ( editor )
     32                return;
     33
     34        var html = document.getElementById( 'editorcontents' ).innerHTML;
     35
     36        editor = CKEDITOR.appendTo( 'editor', config, html );
     37}
     38
     39function removeEditor()
     40{
     41        if ( !editor )
     42                return;
     43
     44        document.getElementById( 'editorcontents' ).innerHTML = editor.getData();
     45//      document.getElementById( 'contents' ).style.display = '';
     46
     47        // Destroy the editor.
     48        editor.destroy();
     49        editor = null;
     50}
     51
     52function initTestLeak()
     53{
     54        document.getElementById( 'output' ).innerHTML = '';
     55        document.getElementById( 'init' ).disabled = true;
     56
     57        garbageDetector.startLeaksCheck();
     58
     59        // Create the instance to test
     60        createEditor();
     61        document.getElementById( 'end' ).disabled = false;
     62}
     63
     64function endTestLeak()
     65{
     66        if (!editor)
     67        {
     68                document.getElementById( 'init' ).disabled = false;
     69                document.getElementById( 'end' ).disabled = true;
     70                return;
     71        }
     72
     73        editor.on( 'destroy' , function () {
     74                window.setTimeout( checkLeaks, 50 );
     75        } );
     76
     77        removeEditor();
     78}
     79
     80// Do the analysis
     81function checkLeaks()
     82{
     83        var text = garbageDetector.endLeaksCheck();
     84        document.getElementById( 'output' ).innerHTML = text;
     85
     86        document.getElementById( 'init' ).disabled = false;
     87        document.getElementById( 'end' ).disabled = true;
     88}
     89
     90
     91window.onload = function()
     92{
     93        garbageDetector.init()
     94
     95        // Create a first instance to initialize the global objects
     96        // We are interested in what's created after that.
     97        var initConfig = {
     98                on : { 'instanceReady' : function( evt )
     99                                                {
     100                                                        window.setTimeout( removeEditor, 200 );
     101                                                        window.setTimeout( initTestLeak, 500 );
     102                                                }
     103                                }
     104        }
     105
     106        editor = CKEDITOR.appendTo( 'editor', initConfig, '' );
     107}
     108
     109// Fills the editor with 1Mb of data
     110function testSetData()
     111{
     112        if (!editor) return;
     113
     114        var line = CKEDITOR.tools.repeat( 'a', 100 ) + '<br>';
     115        var value = CKEDITOR.tools.repeat( line, 10 );
     116
     117        editor.setData( value );
     118}
     119        //]]>
     120        </script>
     121        <style type="text/css">
     122        h2 {
     123                margin-bottom:0.4em;
     124        }
     125        .stack {
     126                white-space:pre;
     127                font-size:80%;
     128        }
     129        </style>
     130</head>
     131<body>
     132        <h1>
     133                CKEditor leak detector
     134        </h1>
     135
     136        <p>
     137                <input onclick="initTestLeak();" id="init" type="button" value="Start leak check" />
     138                <input onclick="endTestLeak();" id="end" type="button" value="End leak check" disabled="disabled"/>
     139        </p>
     140        <fieldset><legend>Tests</legend>
     141                <input type="button" onclick="testSetData()" value="Test setData" />
     142        </fieldset>
     143
     144        <!-- This div will hold the editor. -->
     145        <div id="editor">
     146        </div>
     147        <div id="contents" style="display: none">
     148                <p>
     149                        Edited Contents:</p>
     150                <!-- This div will be used to display the editor contents. -->
     151                <div id="editorcontents">
     152                </div>
     153        </div>
     154        <div id="output"></div>
     155        <div id="footer">
     156                <hr />
     157                <p>
     158                        CKEditor - The text editor for Internet - <a href="http://ckeditor.com/">http://ckeditor.com</a>
     159                </p>
     160                <p id="copy">
     161                        Copyright &copy; 2003-2010, <a href="http://cksource.com/">CKSource</a> - Frederico
     162                        Knabben. All rights reserved.
     163                </p>
     164        </div>
     165</body>
     166</html>
  • _dev/leakDetector/stacktrace.js

     
     1// Domain Public by Eric Wendelin http://eriwen.com/ (2008)
     2//                  Luke Smith http://lucassmith.name/ (2008)
     3//                  Loic Dachary <loic@dachary.org> (2008)
     4//                  Johan Euphrosine <proppy@aminche.com> (2008)
     5//
     6// Information and discussions
     7// http://jspoker.pokersource.info/skin/test-printstacktrace.html
     8// http://eriwen.com/javascript/js-stack-trace/
     9// http://pastie.org/253058
     10// http://browsershots.org/http://jspoker.pokersource.info/skin/test-printstacktrace.html
     11//
     12
     13//
     14// guessFunctionNameFromLines comes from firebug
     15//
     16// Software License Agreement (BSD License)
     17//
     18// Copyright (c) 2007, Parakey Inc.
     19// All rights reserved.
     20//
     21// Redistribution and use of this software in source and binary forms, with or without modification,
     22// are permitted provided that the following conditions are met:
     23//
     24// * Redistributions of source code must retain the above
     25//   copyright notice, this list of conditions and the
     26//   following disclaimer.
     27//
     28// * Redistributions in binary form must reproduce the above
     29//   copyright notice, this list of conditions and the
     30//   following disclaimer in the documentation and/or other
     31//   materials provided with the distribution.
     32//
     33// * Neither the name of Parakey Inc. nor the names of its
     34//   contributors may be used to endorse or promote products
     35//   derived from this software without specific prior
     36//   written permission of Parakey Inc.
     37//
     38// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
     39// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
     40// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     41// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     42// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     43// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
     44// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     45// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     46
     47function printStackTrace(options) {
     48    if (options && options.guess) {
     49        var p = new printStackTrace.implementation();
     50        var result = p.run();
     51        return p.guessFunctions(result);
     52    }
     53    return (new printStackTrace.implementation()).run();
     54}
     55
     56printStackTrace.implementation = function() {};
     57
     58printStackTrace.implementation.prototype = {
     59    run: function() {
     60        mode = this.mode();
     61        if(mode != 'other') {
     62            try {(0)();} catch (e) {
     63                return this[mode](e);
     64            }
     65        } else {
     66            return this.other(arguments.callee);
     67        }
     68    },
     69
     70    mode: function() {
     71        var mode;
     72        try {(0)();} catch (e) {
     73                if (e.arguments) {
     74                        mode = 'chrome';
     75                } else if (e.stack) {
     76                        mode = 'firefox';
     77            } else {
     78                mode = 'other';
     79            }
     80        }
     81        return mode;
     82    },
     83
     84    chrome: function(e) {
     85        return e.stack.replace(/^.*?\n/,'').
     86        replace(/^.*?\n/,'').
     87        replace(/^.*?\n/,'').
     88        replace(/^[^\(]+?[\n$]/gm,'').
     89        replace(/^\s+at\s+/gm,'').
     90        replace(/^Object.<anonymous>/gm,'{anonymous}()').
     91        split("\n");
     92    },
     93
     94    firefox: function(e) {
     95        return e.stack.replace(/^.*?\n/,'').
     96        replace(/(?:\n@:0)?\s+$/m,'').
     97        replace(/^\(/gm,'{anonymous}(').
     98        split("\n");
     99    },
     100
     101    other: function(curr) {
     102        var ANON = "{anonymous}",
     103        fnRE  = /function\s*([\w\-$]+)?\s*\(/i,
     104        stack = [],j=0,fn,args;
     105
     106                var maxStackSize = 7;
     107        while (curr && stack.length < maxStackSize) {
     108            fn = fnRE.test(curr.toString()) ? RegExp.$1 || ANON : ANON;
     109            args = Array.prototype.slice.call(curr['arguments']);
     110            stack[j++] = fn + '(' + printStackTrace.implementation.prototype.stringifyArguments(args) + ')';
     111
     112                        //Opera bug: if curr.caller does not exist, Opera returns curr (WTF)
     113            if (curr === curr.caller && window.opera) {
     114                    break;
     115                }
     116                curr = curr.caller;
     117        }
     118        return stack;
     119    },
     120
     121    stringifyArguments: function(args) {
     122        for (var i = 0; i < args.length; ++i) {
     123            var argument = args[i];
     124            if (typeof argument  == 'object') {
     125                args[i] = '#object';
     126            } else if (typeof argument == 'function') {
     127                args[i] = '#function';
     128            } else if (typeof argument == 'string') {
     129                args[i] = '"'+argument+'"';
     130            }
     131        }
     132        return args.join(',');
     133    },
     134
     135    sourceCache: {},
     136
     137    ajax: function(url) {
     138            var req = this.createXMLHTTPObject();
     139            if (!req) {
     140                return;
     141            }
     142            req.open('GET', url, false);
     143            req.setRequestHeader("User-Agent", "XMLHTTP/1.0");
     144            req.send('');
     145                return req.responseText;
     146    },
     147
     148        createXMLHTTPObject: function() {
     149                var XMLHttpFactories = [
     150                    function () {
     151                        return new XMLHttpRequest();
     152                    },
     153                    function () {
     154                        return new ActiveXObject("Msxml2.XMLHTTP");
     155                    },
     156                    function () {
     157                        return new ActiveXObject("Msxml3.XMLHTTP");
     158                    },
     159                    function () {
     160                        return new ActiveXObject("Microsoft.XMLHTTP");
     161                    }
     162                ];
     163            var xmlhttp = false;
     164            for (var i = 0; i < XMLHttpFactories.length; i++) {
     165                try {
     166                    xmlhttp = XMLHttpFactories[i]();
     167                }
     168                catch (e) {
     169                    e = null;
     170                    continue;
     171                }
     172                break;
     173            }
     174            return xmlhttp;
     175        },
     176
     177    getSource: function(url) {
     178        var self = this;
     179        if (!(url in self.sourceCache)) {
     180            self.sourceCache[url] = self.ajax(url).split("\n");
     181        }
     182        return self.sourceCache[url];
     183    },
     184
     185    guessFunctions: function(stack) {
     186        for (var i = 0; i < stack.length; ++i) {
     187            var reStack = /{anonymous}\(.*\)@(.*):(\d+)/;
     188            var frame = stack[i];
     189            var m = reStack.exec(frame);
     190            if (m) {
     191                var file = m[1];
     192                var lineno = m[2];
     193                if (file && lineno) {
     194                    var functionName = this.guessFunctionName(file, lineno);
     195                    stack[i] = frame.replace('{anonymous}', functionName);
     196                }
     197            }
     198        }
     199        return stack;
     200    },
     201
     202    guessFunctionName: function(url, lineNo) {
     203        var source;
     204        try {
     205            source = this.getSource(url);
     206        } catch (e) {
     207            return 'getSource failed with url: ' + url + ', exception: ' + e.toString();
     208        }
     209        return this.guessFunctionNameFromLines(lineNo, source);
     210    },
     211
     212    guessFunctionNameFromLines: function(lineNo, source) {
     213        var reFunctionArgNames = /function ([^(]*)\(([^)]*)\)/;
     214        var reGuessFunction = /['"]?([0-9A-Za-z_]+)['"]?\s*[:=]\s*(function|eval|new Function)/;
     215        // Walk backwards from the first line in the function until we find the line which
     216        // matches the pattern above, which is the function definition
     217        var line = "";
     218        var maxLines = 10;
     219        for (var i = 0; i < maxLines; ++i) {
     220            line = source[lineNo-i] + line;
     221            if (line !== undefined) {
     222                var m = reGuessFunction.exec(line);
     223                if (m) {
     224                    return m[1];
     225                } else {
     226                    m = reFunctionArgNames.exec(line);
     227                }
     228                if (m && m[1]) {
     229                    return m[1];
     230                }
     231            }
     232        }
     233        return "(?)";
     234    }
     235};
© 2003 – 2019 CKSource – Frederico Knabben. All rights reserved. | Terms of use | Privacy policy