1 /* 2 Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved. 3 For licensing, see LICENSE.html or http://ckeditor.com/license 4 */ 5 6 /** 7 * @fileOverview Defines the {@link CKEDITOR.scriptLoader} object, used to load scripts 8 * asynchronously. 9 */ 10 11 /** 12 * Load scripts asynchronously. 13 * @namespace 14 * @example 15 */ 16 CKEDITOR.scriptLoader = (function() 17 { 18 var uniqueScripts = {}; 19 var waitingList = {}; 20 21 return /** @lends CKEDITOR.scriptLoader */ { 22 /** 23 * Loads one or more external script checking if not already loaded 24 * previously by this function. 25 * @param {String|Array} scriptUrl One or more URLs pointing to the 26 * scripts to be loaded. 27 * @param {Function} [callback] A function to be called when the script 28 * is loaded and executed. If a string is passed to "scriptUrl", a 29 * boolean parameter is passed to the callback, indicating the 30 * success of the load. If an array is passed instead, two array 31 * parameters are passed to the callback; the first contains the 32 * URLs that have been properly loaded, and the second the failed 33 * ones. 34 * @param {Object} [scope] The scope ("this" reference) to be used for 35 * the callback call. Default to {@link CKEDITOR}. 36 * @param {Boolean} [noCheck] Indicates that the script must be loaded 37 * anyway, not checking if it has already loaded. 38 * @example 39 * CKEDITOR.scriptLoader.load( '/myscript.js' ); 40 * @example 41 * CKEDITOR.scriptLoader.load( '/myscript.js', function( success ) 42 * { 43 * // Alerts "true" if the script has been properly loaded. 44 * // HTTP error 404 should return "false". 45 * alert( success ); 46 * }); 47 * @example 48 * CKEDITOR.scriptLoader.load( [ '/myscript1.js', '/myscript2.js' ], function( completed, failed ) 49 * { 50 * alert( 'Number of scripts loaded: ' + completed.length ); 51 * alert( 'Number of failures: ' + failed.length ); 52 * }); 53 */ 54 load : function( scriptUrl, callback, scope, noCheck ) 55 { 56 var isString = ( typeof scriptUrl == 'string' ); 57 58 if ( isString ) 59 scriptUrl = [ scriptUrl ]; 60 61 if ( !scope ) 62 scope = CKEDITOR; 63 64 var scriptCount = scriptUrl.length, 65 completed = [], 66 failed = []; 67 68 var doCallback = function( success ) 69 { 70 if ( callback ) 71 { 72 if ( isString ) 73 callback.call( scope, success ); 74 else 75 callback.call( scope, completed, failed ); 76 } 77 }; 78 79 if ( scriptCount === 0 ) 80 { 81 doCallback( true ); 82 return; 83 } 84 85 var checkLoaded = function( url, success ) 86 { 87 ( success ? completed : failed).push( url ); 88 89 if ( --scriptCount <= 0 ) 90 doCallback( success ); 91 }; 92 93 var onLoad = function( url, success ) 94 { 95 // Mark this script as loaded. 96 uniqueScripts[ url ] = 1; 97 98 // Get the list of callback checks waiting for this file. 99 var waitingInfo = waitingList[ url ]; 100 delete waitingList[ url ]; 101 102 // Check all callbacks waiting for this file. 103 for ( var i = 0 ; i < waitingInfo.length ; i++ ) 104 waitingInfo[ i ]( url, success ); 105 }; 106 107 var loadScript = function( url ) 108 { 109 if ( noCheck !== true && uniqueScripts[ url ] ) 110 { 111 checkLoaded( url, true ); 112 return; 113 } 114 115 var waitingInfo = waitingList[ url ] || ( waitingList[ url ] = [] ); 116 waitingInfo.push( checkLoaded ); 117 118 // Load it only for the first request. 119 if ( waitingInfo.length > 1 ) 120 return; 121 122 // Create the <script> element. 123 var script = new CKEDITOR.dom.element( 'script' ); 124 script.setAttributes( { 125 type : 'text/javascript', 126 src : url } ); 127 128 if ( callback ) 129 { 130 if ( CKEDITOR.env.ie ) 131 { 132 // FIXME: For IE, we are not able to return false on error (like 404). 133 134 /** @ignore */ 135 script.$.onreadystatechange = function () 136 { 137 if ( script.$.readyState == 'loaded' || script.$.readyState == 'complete' ) 138 { 139 script.$.onreadystatechange = null; 140 onLoad( url, true ); 141 } 142 }; 143 } 144 else 145 { 146 /** @ignore */ 147 script.$.onload = function() 148 { 149 onLoad( url, true ); 150 }; 151 152 // FIXME: Opera and Safari will not fire onerror. 153 154 /** @ignore */ 155 script.$.onerror = function() 156 { 157 onLoad( url, false ); 158 }; 159 } 160 } 161 162 // Append it to <head>. 163 script.appendTo( CKEDITOR.document.getHead() ); 164 165 CKEDITOR.fire( 'download', url ); // @Packager.RemoveLine 166 }; 167 168 for ( var i = 0 ; i < scriptCount ; i++ ) 169 { 170 loadScript( scriptUrl[ i ] ); 171 } 172 }, 173 174 /** 175 * Executes a JavaScript code into the current document. 176 * @param {String} code The code to be executed. 177 * @example 178 * CKEDITOR.scriptLoader.loadCode( 'var x = 10;' ); 179 * alert( x ); // "10" 180 */ 181 loadCode : function( code ) 182 { 183 // Create the <script> element. 184 var script = new CKEDITOR.dom.element( 'script' ); 185 script.setAttribute( 'type', 'text/javascript' ); 186 script.appendText( code ); 187 188 // Append it to <head>. 189 script.appendTo( CKEDITOR.document.getHead() ); 190 } 191 }; 192 })(); 193