4 * Copyright 2009, Moxiecode Systems AB
5 * Released under LGPL License.
7 * License: http://tinymce.moxiecode.com/license
8 * Contributing: http://tinymce.moxiecode.com/contributing
12 var whiteSpaceRe = /^\s*|\s*$/g,
13 undefined, isRegExpBroken = 'B'.replace(/A(.)|B/, '$1') === '$1';
16 * Core namespace with core functionality for the TinyMCE API all sub classes will be added to this namespace/object.
21 * // Using each method
22 * tinymce.each([1, 2, 3], function(v, i) {
23 * console.log(i + '=' + v);
26 * // Checking for a specific browser
32 * Major version of TinyMCE build.
34 * @property majorVersion
37 majorVersion : '@@tinymce_major_version@@',
40 * Major version of TinyMCE build.
42 * @property minorVersion
45 minorVersion : '@@tinymce_minor_version@@',
48 * Release date of TinyMCE build.
50 * @property releaseDate
53 releaseDate : '@@tinymce_release_date@@',
56 * Initializes the TinyMCE global namespace this will setup browser detection and figure out where TinyMCE is running from.
59 var t = this, d = document, na = navigator, ua = na.userAgent, i, nl, n, base, p, v;
62 * Constant that is true if the browser is Opera.
68 t.isOpera = win.opera && opera.buildNumber;
71 * Constant that is true if the browser is WebKit (Safari/Chrome).
77 t.isWebKit = /WebKit/.test(ua);
80 * Constant that is true if the browser is IE.
86 t.isIE = !t.isWebKit && !t.isOpera && (/MSIE/gi).test(ua) && (/Explorer/gi).test(na.appName);
89 * Constant that is true if the browser is IE 6 or older.
95 t.isIE6 = t.isIE && /MSIE [56]/.test(ua);
98 * Constant that is true if the browser is IE 7.
104 t.isIE7 = t.isIE && /MSIE [7]/.test(ua);
107 * Constant that is true if the browser is IE 8.
113 t.isIE8 = t.isIE && /MSIE [8]/.test(ua);
116 * Constant that is true if the browser is IE 9.
122 t.isIE9 = t.isIE && /MSIE [9]/.test(ua);
125 * Constant that is true if the browser is Gecko.
131 t.isGecko = !t.isWebKit && /Gecko/.test(ua);
134 * Constant that is true if the os is Mac OS.
140 t.isMac = ua.indexOf('Mac') != -1;
143 * Constant that is true if the runtime is Adobe Air.
149 t.isAir = /adobeair/i.test(ua);
152 * Constant that tells if the current browser is an iPhone or iPad.
154 * @property isIDevice
158 t.isIDevice = /(iPad|iPhone)/.test(ua);
161 * Constant that is true if the current browser is running on iOS 5 or greater.
167 t.isIOS5 = t.isIDevice && ua.match(/AppleWebKit\/(\d*)/)[1]>=534;
169 // TinyMCE .NET webcontrol might be setting the values for TinyMCE
170 if (win.tinyMCEPreInit) {
171 t.suffix = tinyMCEPreInit.suffix;
172 t.baseURL = tinyMCEPreInit.base;
173 t.query = tinyMCEPreInit.query;
177 // Get suffix and base
180 // If base element found, add that infront of baseURL
181 nl = d.getElementsByTagName('base');
182 for (i=0; i<nl.length; i++) {
183 if (v = nl[i].href) {
184 // Host only value like http://site.com or http://site.com:8008
185 if (/^https?:\/\/[^\/]+$/.test(v))
188 base = v ? v.match(/.*\//)[0] : ''; // Get only directory
192 function getBase(n) {
193 if (n.src && /tiny_mce(|_gzip|_jquery|_prototype|_full)(_dev|_src)?.js/.test(n.src)) {
194 if (/_(src|dev)\.js/g.test(n.src))
197 if ((p = n.src.indexOf('?')) != -1)
198 t.query = n.src.substring(p + 1);
200 t.baseURL = n.src.substring(0, n.src.lastIndexOf('/'));
202 // If path to script is relative and a base href was found add that one infront
203 // the src property will always be an absolute one on non IE browsers and IE 8
204 // so this logic will basically only be executed on older IE versions
205 if (base && t.baseURL.indexOf('://') == -1 && t.baseURL.indexOf('/') !== 0)
206 t.baseURL = base + t.baseURL;
215 nl = d.getElementsByTagName('script');
216 for (i=0; i<nl.length; i++) {
222 n = d.getElementsByTagName('head')[0];
224 nl = n.getElementsByTagName('script');
225 for (i=0; i<nl.length; i++) {
235 * Checks if a object is of a specific type for example an array.
238 * @param {Object} o Object to check type of.
239 * @param {string} t Optional type to check for.
240 * @return {Boolean} true/false if the object is of the specified type.
242 is : function(o, t) {
244 return o !== undefined;
246 if (t == 'array' && (o.hasOwnProperty && o instanceof Array))
249 return typeof(o) == t;
253 * Makes a name/object map out of an array with names.
256 * @param {Array/String} items Items to make map out of.
257 * @param {String} delim Optional delimiter to split string by.
258 * @param {Object} map Optional map to add items to.
259 * @return {Object} Name/value map of items.
261 makeMap : function(items, delim, map) {
265 delim = delim || ',';
267 if (typeof(items) == "string")
268 items = items.split(delim);
280 * Performs an iteration of all items in a collection such as an object or array. This method will execure the
281 * callback function for each item in the collection, if the callback returns false the iteration will terminate.
282 * The callback has the following format: cb(value, key_or_index).
285 * @param {Object} o Collection to iterate.
286 * @param {function} cb Callback function to execute for each item.
287 * @param {Object} s Optional scope to execute the callback in.
289 * // Iterate an array
290 * tinymce.each([1,2,3], function(v, i) {
291 * console.debug("Value: " + v + ", Index: " + i);
294 * // Iterate an object
295 * tinymce.each({a : 1, b : 2, c: 3], function(v, k) {
296 * console.debug("Value: " + v + ", Key: " + k);
299 each : function(o, cb, s) {
307 if (o.length !== undefined) {
308 // Indexed arrays, needed for Safari
309 for (n=0, l = o.length; n < l; n++) {
310 if (cb.call(s, o[n], n, o) === false)
316 if (o.hasOwnProperty(n)) {
317 if (cb.call(s, o[n], n, o) === false)
329 * Creates a new array by the return value of each iteration function call. This enables you to convert
330 * one array list into another.
333 * @param {Array} a Array of items to iterate.
334 * @param {function} f Function to call for each item. It's return value will be the new value.
335 * @return {Array} Array with new values based on function return values.
337 map : function(a, f) {
340 tinymce.each(a, function(v) {
348 * Filters out items from the input array by calling the specified function for each item.
349 * If the function returns false the item will be excluded if it returns true it will be included.
352 * @param {Array} a Array of items to loop though.
353 * @param {function} f Function to call for each item. Include/exclude depends on it's return value.
354 * @return {Array} New array with values imported and filtered based in input.
356 * // Filter out some items, this will return an array with 4 and 5
357 * var items = tinymce.grep([1,2,3,4,5], function(v) {return v > 3;});
359 grep : function(a, f) {
362 tinymce.each(a, function(v) {
371 * Returns the index of a value in an array, this method will return -1 if the item wasn't found.
374 * @param {Array} a Array/Object to search for value in.
375 * @param {Object} v Value to check for inside the array.
376 * @return {Number/String} Index of item inside the array inside an object. Or -1 if it wasn't found.
378 * // Get index of value in array this will alert 1 since 2 is at that index
379 * alert(tinymce.inArray([1,2,3], 2));
381 inArray : function(a, v) {
385 for (i = 0, l = a.length; i < l; i++) {
395 * Extends an object with the specified other object(s).
398 * @param {Object} o Object to extend with new items.
399 * @param {Object} e..n Object(s) to extend the specified object with.
400 * @return {Object} o New extended object, same reference as the input object.
402 * // Extends obj1 with two new fields
403 * var obj = tinymce.extend(obj1, {
408 * // Extends obj with obj2 and obj3
409 * tinymce.extend(obj, obj2, obj3);
411 extend : function(o, e) {
412 var i, l, a = arguments;
414 for (i = 1, l = a.length; i < l; i++) {
417 tinymce.each(e, function(v, n) {
429 * Removes whitespace from the beginning and end of a string.
432 * @param {String} s String to remove whitespace from.
433 * @return {String} New string with removed whitespace.
436 return (s ? '' + s : '').replace(whiteSpaceRe, '');
440 * Creates a class, subclass or static singleton.
441 * More details on this method can be found in the Wiki.
444 * @param {String} s Class name, inheritage and prefix.
445 * @param {Object} p Collection of methods to add to the class.
446 * @param {Object} root Optional root object defaults to the global window object.
448 * // Creates a basic class
449 * tinymce.create('tinymce.somepackage.SomeClass', {
450 * SomeClass : function() {
451 * // Class constructor
454 * method : function() {
459 * // Creates a basic subclass class
460 * tinymce.create('tinymce.somepackage.SomeSubClass:tinymce.somepackage.SomeClass', {
461 * SomeSubClass: function() {
462 * // Class constructor
463 * this.parent(); // Call parent constructor
466 * method : function() {
468 * this.parent(); // Call parent method
472 * staticMethod : function() {
478 * // Creates a singleton/static class
479 * tinymce.create('static tinymce.somepackage.SomeSingletonClass', {
480 * method : function() {
485 create : function(s, p, root) {
486 var t = this, sp, ns, cn, scn, c, de = 0;
488 // Parse : <prefix> <class>:<super class>
489 s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s);
490 cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name
492 // Create namespace for new class
493 ns = t.createNS(s[3].replace(/\.\w+$/, ''), root);
495 // Class already exists
499 // Make pure static class
500 if (s[2] == 'static') {
504 this.onCreate(s[2], s[3], ns[cn]);
509 // Create default constructor
511 p[cn] = function() {};
515 // Add constructor and methods
517 t.extend(ns[cn].prototype, p);
521 sp = t.resolve(s[5]).prototype;
522 scn = s[5].match(/\.(\w+)$/i)[1]; // Class name
524 // Extend constructor
527 // Add passthrough constructor
528 ns[cn] = function() {
529 return sp[scn].apply(this, arguments);
532 // Add inherit constructor
533 ns[cn] = function() {
534 this.parent = sp[scn];
535 return c.apply(this, arguments);
538 ns[cn].prototype[cn] = ns[cn];
541 t.each(sp, function(f, n) {
542 ns[cn].prototype[n] = sp[n];
545 // Add overridden methods
546 t.each(p, function(f, n) {
547 // Extend methods if needed
549 ns[cn].prototype[n] = function() {
551 return f.apply(this, arguments);
555 ns[cn].prototype[n] = f;
560 // Add static methods
561 t.each(p['static'], function(f, n) {
566 this.onCreate(s[2], s[3], ns[cn].prototype);
570 * Executed the specified function for each item in a object tree.
573 * @param {Object} o Object tree to walk though.
574 * @param {function} f Function to call for each item.
575 * @param {String} n Optional name of collection inside the objects to walk for example childNodes.
576 * @param {String} s Optional scope to execute the function in.
578 walk : function(o, f, n, s) {
585 tinymce.each(o, function(o, i) {
586 if (f.call(s, o, i, n) === false)
589 tinymce.walk(o, f, n, s);
595 * Creates a namespace on a specific object.
598 * @param {String} n Namespace to create for example a.b.c.d.
599 * @param {Object} o Optional object to add namespace to, defaults to window.
600 * @return {Object} New namespace object the last item in path.
602 * // Create some namespace
603 * tinymce.createNS('tinymce.somepackage.subpackage');
606 * var tinymce.somepackage.subpackage.SomeSingleton = {
607 * method : function() {
612 createNS : function(n, o) {
618 for (i=0; i<n.length; i++) {
631 * Resolves a string and returns the object from a specific structure.
634 * @param {String} n Path to resolve for example a.b.c.d.
635 * @param {Object} o Optional object to search though, defaults to window.
636 * @return {Object} Last object in path or null if it couldn't be resolved.
638 * // Resolve a path into an object reference
639 * var obj = tinymce.resolve('a.b.c.d');
641 resolve : function(n, o) {
647 for (i = 0, l = n.length; i < l; i++) {
658 * Adds an unload handler to the document. This handler will be executed when the document gets unloaded.
659 * This method is useful for dealing with browser memory leaks where it might be vital to remove DOM references etc.
662 * @param {function} f Function to execute before the document gets unloaded.
663 * @param {Object} s Optional scope to execute the function in.
664 * @return {function} Returns the specified unload handler function.
666 * // Fixes a leak with a DOM element that was palces in the someObject
667 * tinymce.addUnload(function() {
668 * // Null DOM element to reduce IE memory leak
669 * someObject.someElement = null;
672 addUnload : function(f, s) {
675 f = {func : f, scope : s || this};
679 var li = t.unloads, o, n;
682 // Call unload handlers
687 o.func.call(o.scope, 1); // Send in one arg to distinct unload and user destroy
690 // Detach unload function
691 if (win.detachEvent) {
692 win.detachEvent('onbeforeunload', fakeUnload);
693 win.detachEvent('onunload', unload);
694 } else if (win.removeEventListener)
695 win.removeEventListener('unload', unload, false);
697 // Destroy references
698 t.unloads = o = li = w = unload = 0;
700 // Run garbarge collector on IE
701 if (win.CollectGarbage)
706 function fakeUnload() {
709 // Is there things still loading, then do some magic
710 if (d.readyState == 'interactive') {
712 // Prevent memory leak
713 d.detachEvent('onstop', stop);
715 // Call unload handler
722 // Fire unload when the currently loading page is stopped
724 d.attachEvent('onstop', stop);
726 // Remove onstop listener after a while to prevent the unload function
727 // to execute if the user presses cancel in an onbeforeunload
728 // confirm dialog and then presses the browser stop button
729 win.setTimeout(function() {
731 d.detachEvent('onstop', stop);
736 // Attach unload handler
737 if (win.attachEvent) {
738 win.attachEvent('onunload', unload);
739 win.attachEvent('onbeforeunload', fakeUnload);
740 } else if (win.addEventListener)
741 win.addEventListener('unload', unload, false);
743 // Setup initial unload handler array
752 * Removes the specified function form the unload handler list.
754 * @method removeUnload
755 * @param {function} f Function to remove from unload handler list.
756 * @return {function} Removed function name or null if it wasn't found.
758 removeUnload : function(f) {
759 var u = this.unloads, r = null;
761 tinymce.each(u, function(o, i) {
762 if (o && o.func == f) {
773 * Splits a string but removes the whitespace before and after each value.
776 * @param {string} s String to split.
777 * @param {string} d Delimiter to split by.
779 * // Split a string into an array with a,b,c
780 * var arr = tinymce.explode('a, b, c');
782 explode : function(s, d) {
783 return s ? tinymce.map(s.split(d || ','), tinymce.trim) : s;
786 _addVer : function(u) {
792 v = (u.indexOf('?') == -1 ? '?' : '&') + this.query;
794 if (u.indexOf('#') == -1)
797 return u.replace('#', v + '#');
800 // Fix function for IE 9 where regexps isn't working correctly
801 // Todo: remove me once MS fixes the bug
802 _replace : function(find, replace, str) {
803 // On IE9 we have to fake $x replacement
804 if (isRegExpBroken) {
805 return str.replace(find, function() {
806 var val = replace, args = arguments, i;
808 for (i = 0; i < args.length - 2; i++) {
809 if (args[i] === undefined) {
810 val = val.replace(new RegExp('\\$' + i, 'g'), '');
812 val = val.replace(new RegExp('\\$' + i, 'g'), args[i]);
820 return str.replace(find, replace);
826 // Initialize the API
829 // Expose tinymce namespace to the global namespace (window)
830 win.tinymce = win.tinyMCE = tinymce;
832 // Describe the different namespaces
835 * Root level namespace this contains classes directly releated to the TinyMCE editor.
841 * Contains classes for handling the browsers DOM.
843 * @namespace tinymce.dom
847 * Contains html parser and serializer logic.
849 * @namespace tinymce.html
853 * Contains the different UI types such as buttons, listboxes etc.
855 * @namespace tinymce.ui
859 * Contains various utility classes such as json parser, cookies etc.
861 * @namespace tinymce.util
865 * Contains plugin classes.
867 * @namespace tinymce.plugins