Ext-more.js
上传用户:shuoshiled
上传日期:2018-01-28
资源大小:10124k
文件大小:24k
源码类别:

中间件编程

开发平台:

JavaScript

  1. /*!
  2.  * Ext JS Library 3.0.0
  3.  * Copyright(c) 2006-2009 Ext JS, LLC
  4.  * licensing@extjs.com
  5.  * http://www.extjs.com/license
  6.  */
  7. /**
  8.  * @class Ext
  9.  */
  10. Ext.ns("Ext.grid", "Ext.dd", "Ext.tree", "Ext.form", "Ext.menu",
  11.        "Ext.state", "Ext.layout", "Ext.app", "Ext.ux", "Ext.chart", "Ext.direct");
  12.     /**
  13.      * Namespace alloted for extensions to the framework.
  14.      * @property ux
  15.      * @type Object
  16.      */
  17. Ext.apply(Ext, function(){
  18.     var E = Ext, idSeed = 0;
  19.     return {
  20.         /**
  21.         * A reusable empty function
  22.         * @property
  23.         * @type Function
  24.         */
  25.         emptyFn : function(){},
  26.         /**
  27.          * URL to a 1x1 transparent gif image used by Ext to create inline icons with CSS background images. 
  28.          * In older versions of IE, this defaults to "http://extjs.com/s.gif" and you should change this to a URL on your server.
  29.          * For other browsers it uses an inline data URL.
  30.          * @type String
  31.          */
  32.         BLANK_IMAGE_URL : Ext.isIE6 || Ext.isIE7 ?
  33.                             'http:/' + '/extjs.com/s.gif' :
  34.                             'data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',
  35.         extendX : function(supr, fn){
  36.             return Ext.extend(supr, fn(supr.prototype));
  37.         },
  38.         /**
  39.          * Returns the current HTML document object as an {@link Ext.Element}.
  40.          * @return Ext.Element The document
  41.          */
  42.         getDoc : function(){
  43.             return Ext.get(document);
  44.         },
  45.         /**
  46.          * Returns true if the passed object is a JavaScript date object, otherwise false.
  47.          * @param {Object} object The object to test
  48.          * @return {Boolean}
  49.          */
  50.         isDate : function(v){
  51.             return Object.prototype.toString.apply(v) === '[object Date]';
  52.         },
  53.         /**
  54.          * Utility method for validating that a value is numeric, returning the specified default value if it is not.
  55.          * @param {Mixed} value Should be a number, but any type will be handled appropriately
  56.          * @param {Number} defaultValue The value to return if the original value is non-numeric
  57.          * @return {Number} Value, if numeric, else defaultValue
  58.          */
  59.         num : function(v, defaultValue){
  60.             v = Number(v === null || typeof v == 'boolean'? NaN : v);
  61.             return isNaN(v)? defaultValue : v;
  62.         },
  63.         /**
  64.          * <p>Utility method for returning a default value if the passed value is empty.</p>
  65.          * <p>The value is deemed to be empty if it is<div class="mdetail-params"><ul>
  66.          * <li>null</li>
  67.          * <li>undefined</li>
  68.          * <li>an empty array</li>
  69.          * <li>a zero length string (Unless the <tt>allowBlank</tt> parameter is <tt>true</tt>)</li>
  70.          * </ul></div>
  71.          * @param {Mixed} value The value to test
  72.          * @param {Mixed} defaultValue The value to return if the original value is empty
  73.          * @param {Boolean} allowBlank (optional) true to allow zero length strings to qualify as non-empty (defaults to false)
  74.          * @return {Mixed} value, if non-empty, else defaultValue
  75.          */
  76.         value : function(v, defaultValue, allowBlank){
  77.             return Ext.isEmpty(v, allowBlank) ? defaultValue : v;
  78.         },
  79.         /**
  80.          * Escapes the passed string for use in a regular expression
  81.          * @param {String} str
  82.          * @return {String}
  83.          */
  84.         escapeRe : function(s) {
  85.             return s.replace(/([.*+?^${}()|[]/\])/g, "\$1");
  86.         },
  87.         sequence : function(o, name, fn, scope){
  88.             o[name] = o[name].createSequence(fn, scope);
  89.         },
  90.         /**
  91.          * Applies event listeners to elements by selectors when the document is ready.
  92.          * The event name is specified with an <tt>&#64;</tt> suffix.
  93.          * <pre><code>
  94. Ext.addBehaviors({
  95.     // add a listener for click on all anchors in element with id foo
  96.     '#foo a&#64;click' : function(e, t){
  97.         // do something
  98.     },
  99.     
  100.     // add the same listener to multiple selectors (separated by comma BEFORE the &#64;)
  101.     '#foo a, #bar span.some-class&#64;mouseover' : function(){
  102.         // do something
  103.     }
  104. });
  105.          * </code></pre> 
  106.          * @param {Object} obj The list of behaviors to apply
  107.          */
  108.         addBehaviors : function(o){
  109.             if(!Ext.isReady){
  110.                 Ext.onReady(function(){
  111.                     Ext.addBehaviors(o);
  112.                 });
  113.             } else {
  114.                 var cache = {}, // simple cache for applying multiple behaviors to same selector does query multiple times
  115.                     parts,
  116.                     b,
  117.                     s;
  118.                 for (b in o) {
  119.                     if ((parts = b.split('@'))[1]) { // for Object prototype breakers
  120.                         s = parts[0];
  121.                         if(!cache[s]){
  122.                             cache[s] = Ext.select(s);
  123.                         }
  124.                         cache[s].on(parts[1], o[b]);
  125.                     }
  126.                 }
  127.                 cache = null;
  128.             }
  129.         },
  130.         // deprecated
  131.         combine : function(){
  132.             var as = arguments, l = as.length, r = [];
  133.             for(var i = 0; i < l; i++){
  134.                 var a = as[i];
  135.                 if(Ext.isArray(a)){
  136.                     r = r.concat(a);
  137.                 }else if(a.length !== undefined && !a.substr){
  138.                     r = r.concat(Array.prototype.slice.call(a, 0));
  139.                 }else{
  140.                     r.push(a);
  141.                 }
  142.             }
  143.             return r;
  144.         },
  145.         /**
  146.          * Copies a set of named properties fom the source object to the destination object.
  147.          * <p>example:<pre><code>
  148. ImageComponent = Ext.extend(Ext.BoxComponent, {
  149.     initComponent: function() {
  150.         this.autoEl = { tag: 'img' };
  151.         MyComponent.superclass.initComponent.apply(this, arguments);
  152.         this.initialBox = Ext.copyTo({}, this.initialConfig, 'x,y,width,height');
  153.     }
  154. });
  155.          * </code></pre> 
  156.          * @param {Object} The destination object.
  157.          * @param {Object} The source object.
  158.          * @param {Array/String} Either an Array of property names, or a comma-delimited list
  159.          * of property names to copy.
  160.          * @return {Object} The modified object.
  161.         */
  162.         copyTo : function(dest, source, names){
  163.             if(typeof names == 'string'){
  164.                 names = names.split(/[,;s]/);
  165.             }
  166.             Ext.each(names, function(name){
  167.                 if(source.hasOwnProperty(name)){
  168.                     dest[name] = source[name];
  169.                 }
  170.             }, this);
  171.             return dest;
  172.         },
  173.         /**
  174.          * Attempts to destroy any objects passed to it by removing all event listeners, removing them from the
  175.          * DOM (if applicable) and calling their destroy functions (if available).  This method is primarily
  176.          * intended for arguments of type {@link Ext.Element} and {@link Ext.Component}, but any subclass of
  177.          * {@link Ext.util.Observable} can be passed in.  Any number of elements and/or components can be
  178.          * passed into this function in a single call as separate arguments.
  179.          * @param {Mixed} arg1 An {@link Ext.Element}, {@link Ext.Component}, or an Array of either of these to destroy
  180.          * @param {Mixed} arg2 (optional)
  181.          * @param {Mixed} etc... (optional)
  182.          */
  183.         destroy : function(){
  184.             Ext.each(arguments, function(arg){
  185.                 if(arg){
  186.                     if(Ext.isArray(arg)){
  187.                         this.destroy.apply(this, arg);
  188.                     }else if(Ext.isFunction(arg.destroy)){
  189.                         arg.destroy();
  190.                     }else if(arg.dom){
  191.                         arg.remove();
  192.                     }    
  193.                 }
  194.             }, this);
  195.         },
  196.         /**
  197.          * Attempts to destroy and then remove a set of named properties of the passed object.
  198.          * @param {Object} o The object (most likely a Component) who's properties you wish to destroy.
  199.          * @param {Mixed} arg1 The name of the property to destroy and remove from the object.
  200.          * @param {Mixed} etc... More property names to destroy and remove.
  201.          */
  202.         destroyMembers : function(o, arg1, arg2, etc){
  203.             for(var i = 1, a = arguments, len = a.length; i < len; i++) {
  204.                 Ext.destroy(o[a[i]]);
  205.                 delete o[a[i]];
  206.             }
  207.         },
  208.         /**
  209.          * Creates a copy of the passed Array with falsy values removed.
  210.          * @param {Array/NodeList} arr The Array from which to remove falsy values.
  211.          * @return {Array} The new, compressed Array.
  212.          */
  213.         clean : function(arr){
  214.             var ret = [];
  215.             Ext.each(arr, function(v){
  216.                 if(!!v){
  217.                     ret.push(v);
  218.                 }
  219.             });
  220.             return ret;
  221.         },
  222.         /**
  223.          * Creates a copy of the passed Array, filtered to contain only unique values.
  224.          * @param {Array} arr The Array to filter
  225.          * @return {Array} The new Array containing unique values.
  226.          */
  227.         unique : function(arr){
  228.             var ret = [],
  229.                 collect = {};
  230.             Ext.each(arr, function(v) {
  231.                 if(!collect[v]){
  232.                     ret.push(v);
  233.                 }
  234.                 collect[v] = true;
  235.             });
  236.             return ret;
  237.         },
  238.         /**
  239.          * Recursively flattens into 1-d Array. Injects Arrays inline.
  240.          * @param {Array} arr The array to flatten
  241.          * @return {Array} The new, flattened array.
  242.          */
  243.         flatten : function(arr){
  244.             var worker = [];
  245.             function rFlatten(a) {
  246.                 Ext.each(a, function(v) {
  247.                     if(Ext.isArray(v)){
  248.                         rFlatten(v);
  249.                     }else{
  250.                         worker.push(v);
  251.                     }
  252.                 });
  253.                 return worker;
  254.             }
  255.             return rFlatten(arr);
  256.         },
  257.         /**
  258.          * Returns the minimum value in the Array.
  259.          * @param {Array|NodeList} arr The Array from which to select the minimum value.
  260.          * @param {Function} comp (optional) a function to perform the comparision which determines minimization.
  261.          *                   If omitted the "<" operator will be used. Note: gt = 1; eq = 0; lt = -1
  262.          * @return {Object} The minimum value in the Array.
  263.          */
  264.         min : function(arr, comp){
  265.             var ret = arr[0];
  266.             comp = comp || function(a,b){ return a < b ? -1 : 1; };
  267.             Ext.each(arr, function(v) {
  268.                 ret = comp(ret, v) == -1 ? ret : v;
  269.             });
  270.             return ret;
  271.         },
  272.         /**
  273.          * Returns the maximum value in the Array
  274.          * @param {Array|NodeList} arr The Array from which to select the maximum value.
  275.          * @param {Function} comp (optional) a function to perform the comparision which determines maximization.
  276.          *                   If omitted the ">" operator will be used. Note: gt = 1; eq = 0; lt = -1
  277.          * @return {Object} The maximum value in the Array.
  278.          */
  279.         max : function(arr, comp){
  280.             var ret = arr[0];
  281.             comp = comp || function(a,b){ return a > b ? 1 : -1; };
  282.             Ext.each(arr, function(v) {
  283.                 ret = comp(ret, v) == 1 ? ret : v;
  284.             });
  285.             return ret;
  286.         },
  287.         /**
  288.          * Calculates the mean of the Array
  289.          * @param {Array} arr The Array to calculate the mean value of.
  290.          * @return {Number} The mean.
  291.          */
  292.         mean : function(arr){
  293.            return Ext.sum(arr) / arr.length;
  294.         },
  295.         /**
  296.          * Calculates the sum of the Array
  297.          * @param {Array} arr The Array to calculate the sum value of.
  298.          * @return {Number} The sum.
  299.          */
  300.         sum : function(arr){
  301.            var ret = 0;
  302.            Ext.each(arr, function(v) {
  303.                ret += v;
  304.            });
  305.            return ret;
  306.         },
  307.         /**
  308.          * Partitions the set into two sets: a true set and a false set.
  309.          * Example: 
  310.          * Example2: 
  311.          * <pre><code>
  312. // Example 1:
  313. Ext.partition([true, false, true, true, false]); // [[true, true, true], [false, false]]
  314. // Example 2:
  315. Ext.partition(
  316.     Ext.query("p"),
  317.     function(val){
  318.         return val.className == "class1"
  319.     }
  320. );
  321. // true are those paragraph elements with a className of "class1",
  322. // false set are those that do not have that className.
  323.          * </code></pre>
  324.          * @param {Array|NodeList} arr The array to partition
  325.          * @param {Function} truth (optional) a function to determine truth.  If this is omitted the element
  326.          *                   itself must be able to be evaluated for its truthfulness.
  327.          * @return {Array} [true<Array>,false<Array>]
  328.          */
  329.         partition : function(arr, truth){
  330.             var ret = [[],[]];
  331.             Ext.each(arr, function(v, i, a) {
  332.                 ret[ (truth && truth(v, i, a)) || (!truth && v) ? 0 : 1].push(v);
  333.             });
  334.             return ret;
  335.         },
  336.         /**
  337.          * Invokes a method on each item in an Array.
  338.          * <pre><code>
  339. // Example:
  340. Ext.invoke(Ext.query("p"), "getAttribute", "id");
  341. // [el1.getAttribute("id"), el2.getAttribute("id"), ..., elN.getAttribute("id")]
  342.          * </code></pre>
  343.          * @param {Array|NodeList} arr The Array of items to invoke the method on.
  344.          * @param {String} methodName The method name to invoke.
  345.          * @param {Anything} ... Arguments to send into the method invocation.
  346.          * @return {Array} The results of invoking the method on each item in the array.
  347.          */
  348.         invoke : function(arr, methodName){
  349.             var ret = [],
  350.                 args = Array.prototype.slice.call(arguments, 2);
  351.             Ext.each(arr, function(v,i) {
  352.                 if (v && typeof v[methodName] == "function") {
  353.                     ret.push(v[methodName].apply(v, args));
  354.                 } else {
  355.                     ret.push(undefined);
  356.                 }
  357.             });
  358.             return ret;
  359.         },
  360.         /**
  361.          * Plucks the value of a property from each item in the Array
  362.          * <pre><code>
  363. // Example:
  364. Ext.pluck(Ext.query("p"), "className"); // [el1.className, el2.className, ..., elN.className]
  365.          * </code></pre>
  366.          * @param {Array|NodeList} arr The Array of items to pluck the value from.
  367.          * @param {String} prop The property name to pluck from each element.
  368.          * @return {Array} The value from each item in the Array.
  369.          */
  370.         pluck : function(arr, prop){
  371.             var ret = [];
  372.             Ext.each(arr, function(v) {
  373.                 ret.push( v[prop] );
  374.             });
  375.             return ret;
  376.         },
  377.         /**
  378.          * <p>Zips N sets together.</p>
  379.          * <pre><code>
  380. // Example 1:
  381. Ext.zip([1,2,3],[4,5,6]); // [[1,4],[2,5],[3,6]]
  382. // Example 2:
  383. Ext.zip(
  384.     [ "+", "-", "+"],
  385.     [  12,  10,  22],
  386.     [  43,  15,  96],
  387.     function(a, b, c){
  388.         return "$" + a + "" + b + "." + c
  389.     }
  390. ); // ["$+12.43", "$-10.15", "$+22.96"]
  391.          * </code></pre>
  392.          * @param {Arrays|NodeLists} arr This argument may be repeated. Array(s) to contribute values.
  393.          * @param {Function} zipper (optional) The last item in the argument list. This will drive how the items are zipped together.
  394.          * @return {Array} The zipped set.
  395.          */
  396.         zip : function(){
  397.             var parts = Ext.partition(arguments, function( val ){ return !Ext.isFunction(val); }),
  398.                 arrs = parts[0],
  399.                 fn = parts[1][0],
  400.                 len = Ext.max(Ext.pluck(arrs, "length")),
  401.                 ret = [];
  402.             for (var i = 0; i < len; i++) {
  403.                 ret[i] = [];
  404.                 if(fn){
  405.                     ret[i] = fn.apply(fn, Ext.pluck(arrs, i));
  406.                 }else{
  407.                     for (var j = 0, aLen = arrs.length; j < aLen; j++){
  408.                         ret[i].push( arrs[j][i] );
  409.                     }
  410.                 }
  411.             }
  412.             return ret;
  413.         },
  414.         /**
  415.          * This is shorthand reference to {@link Ext.ComponentMgr#get}.
  416.          * Looks up an existing {@link Ext.Component Component} by {@link Ext.Component#id id}
  417.          * @param {String} id The component {@link Ext.Component#id id}
  418.          * @return Ext.Component The Component, <tt>undefined</tt> if not found, or <tt>null</tt> if a
  419.          * Class was found.
  420.         */
  421.         getCmp : function(id){
  422.             return Ext.ComponentMgr.get(id);
  423.         },
  424.         /**
  425.          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
  426.          * you may want to set this to true.
  427.          * @type Boolean
  428.          */
  429.         useShims: E.isIE6 || (E.isMac && E.isGecko2),
  430.         // inpired by a similar function in mootools library
  431.         /**
  432.          * Returns the type of object that is passed in. If the object passed in is null or undefined it
  433.          * return false otherwise it returns one of the following values:<div class="mdetail-params"><ul>
  434.          * <li><b>string</b>: If the object passed is a string</li>
  435.          * <li><b>number</b>: If the object passed is a number</li>
  436.          * <li><b>boolean</b>: If the object passed is a boolean value</li>
  437.          * <li><b>date</b>: If the object passed is a Date object</li>
  438.          * <li><b>function</b>: If the object passed is a function reference</li>
  439.          * <li><b>object</b>: If the object passed is an object</li>
  440.          * <li><b>array</b>: If the object passed is an array</li>
  441.          * <li><b>regexp</b>: If the object passed is a regular expression</li>
  442.          * <li><b>element</b>: If the object passed is a DOM Element</li>
  443.          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
  444.          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
  445.          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
  446.          * </ul></div>
  447.          * @param {Mixed} object
  448.          * @return {String}
  449.          */
  450.         type : function(o){
  451.             if(o === undefined || o === null){
  452.                 return false;
  453.             }
  454.             if(o.htmlElement){
  455.                 return 'element';
  456.             }
  457.             var t = typeof o;
  458.             if(t == 'object' && o.nodeName) {
  459.                 switch(o.nodeType) {
  460.                     case 1: return 'element';
  461.                     case 3: return (/S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
  462.                 }
  463.             }
  464.             if(t == 'object' || t == 'function') {
  465.                 switch(o.constructor) {
  466.                     case Array: return 'array';
  467.                     case RegExp: return 'regexp';
  468.                     case Date: return 'date';
  469.                 }
  470.                 if(typeof o.length == 'number' && typeof o.item == 'function') {
  471.                     return 'nodelist';
  472.                 }
  473.             }
  474.             return t;
  475.         },
  476.         intercept : function(o, name, fn, scope){
  477.             o[name] = o[name].createInterceptor(fn, scope);
  478.         },
  479.         // internal
  480.         callback : function(cb, scope, args, delay){
  481.             if(Ext.isFunction(cb)){
  482.                 if(delay){
  483.                     cb.defer(delay, scope, args || []);
  484.                 }else{
  485.                     cb.apply(scope, args || []);
  486.                 }
  487.             }
  488.         }
  489.     };
  490. }());
  491. /**
  492.  * @class Function
  493.  * These functions are available on every Function object (any JavaScript function).
  494.  */
  495. Ext.apply(Function.prototype, {
  496.     /**
  497.      * Create a combined function call sequence of the original function + the passed function.
  498.      * The resulting function returns the results of the original function.
  499.      * The passed fcn is called with the parameters of the original function. Example usage:
  500.      * <pre><code>
  501. var sayHi = function(name){
  502.     alert('Hi, ' + name);
  503. }
  504. sayHi('Fred'); // alerts "Hi, Fred"
  505. var sayGoodbye = sayHi.createSequence(function(name){
  506.     alert('Bye, ' + name);
  507. });
  508. sayGoodbye('Fred'); // both alerts show
  509. </code></pre>
  510.      * @param {Function} fcn The function to sequence
  511.      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
  512.      * @return {Function} The new function
  513.      */
  514.     createSequence : function(fcn, scope){
  515.         var method = this;
  516.         return !Ext.isFunction(fcn) ?
  517.                 this :
  518.                 function(){
  519.                     var retval = method.apply(this || window, arguments);
  520.                     fcn.apply(scope || this || window, arguments);
  521.                     return retval;
  522.                 };
  523.     }
  524. });
  525. /**
  526.  * @class String
  527.  * These functions are available as static methods on the JavaScript String object.
  528.  */
  529. Ext.applyIf(String, {
  530.     /**
  531.      * Escapes the passed string for ' and 
  532.      * @param {String} string The string to escape
  533.      * @return {String} The escaped string
  534.      * @static
  535.      */
  536.     escape : function(string) {
  537.         return string.replace(/('|\)/g, "\$1");
  538.     },
  539.     /**
  540.      * Pads the left side of a string with a specified character.  This is especially useful
  541.      * for normalizing number and date strings.  Example usage:
  542.      * <pre><code>
  543. var s = String.leftPad('123', 5, '0');
  544. // s now contains the string: '00123'
  545.      * </code></pre>
  546.      * @param {String} string The original string
  547.      * @param {Number} size The total length of the output string
  548.      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
  549.      * @return {String} The padded string
  550.      * @static
  551.      */
  552.     leftPad : function (val, size, ch) {
  553.         var result = String(val);
  554.         if(!ch) {
  555.             ch = " ";
  556.         }
  557.         while (result.length < size) {
  558.             result = ch + result;
  559.         }
  560.         return result;
  561.     }
  562. });
  563. /**
  564.  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
  565.  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
  566.  * they are already different, the first value passed in is returned.  Note that this method returns the new value
  567.  * but does not change the current string.
  568.  * <pre><code>
  569. // alternate sort directions
  570. sort = sort.toggle('ASC', 'DESC');
  571. // instead of conditional logic:
  572. sort = (sort == 'ASC' ? 'DESC' : 'ASC');
  573. </code></pre>
  574.  * @param {String} value The value to compare to the current string
  575.  * @param {String} other The new value to use if the string already equals the first value passed in
  576.  * @return {String} The new value
  577.  */
  578. String.prototype.toggle = function(value, other){
  579.     return this == value ? other : value;
  580. };
  581. /**
  582.  * Trims whitespace from either end of a string, leaving spaces within the string intact.  Example:
  583.  * <pre><code>
  584. var s = '  foo bar  ';
  585. alert('-' + s + '-');         //alerts "- foo bar -"
  586. alert('-' + s.trim() + '-');  //alerts "-foo bar-"
  587. </code></pre>
  588.  * @return {String} The trimmed string
  589.  */
  590. String.prototype.trim = function(){
  591.     var re = /^s+|s+$/g;
  592.     return function(){ return this.replace(re, ""); };
  593. }();
  594. // here to prevent dependency on Date.js
  595. /**
  596.  Returns the number of milliseconds between this date and date
  597.  @param {Date} date (optional) Defaults to now
  598.  @return {Number} The diff in milliseconds
  599.  @member Date getElapsed
  600.  */
  601. Date.prototype.getElapsed = function(date) {
  602.     return Math.abs((date || new Date()).getTime()-this.getTime());
  603. };
  604. /**
  605.  * @class Number
  606.  */
  607. Ext.applyIf(Number.prototype, {
  608.     /**
  609.      * Checks whether or not the current number is within a desired range.  If the number is already within the
  610.      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
  611.      * exceeded.  Note that this method returns the constrained value but does not change the current number.
  612.      * @param {Number} min The minimum number in the range
  613.      * @param {Number} max The maximum number in the range
  614.      * @return {Number} The constrained value if outside the range, otherwise the current value
  615.      */
  616.     constrain : function(min, max){
  617.         return Math.min(Math.max(this, min), max);
  618.     }
  619. });