Development

Changeset 19226

You must first sign up to be able to contribute.

Changeset 19226

Show
Ignore:
Timestamp:
06/13/09 16:54:20 (4 years ago)
Author:
joshiausdemwald
Message:

JSON Response

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • plugins/sfWidgetFormInputSWFUploadPlugin/lib/widget/sfWidgetFormInputSWFUpload.php

    r19190 r19226  
    5151    parent::configure($options, $attributes); 
    5252 
     53    $this->addOption('collapse_queue_on_init', true); 
     54 
     55    $this->addOption('send_serialized_values', true); 
    5356    $this->addOption('require_yui', false); 
    5457 
     
    153156 
    154157    $swfupload_post_name = $this->getOption('swfupload_post_name') === null ? $name : $this->getOption('swfupload_post_name'); 
     158 
     159    $send_serialized_values = $this->getOption('send_serialized_values') ? 'true' : 'false'; 
     160 
     161    $collapse_queue_on_init = $this->getOption('collapse_queue_on_init') ? 'true' : 'false'; 
    155162 
    156163    $output .= <<<EOF 
     
    176183            custom_settings : 
    177184            { 
    178               widget_id: "{$widget_id}" 
     185              widget_id: "{$widget_id}", 
     186              send_serialized_values: $send_serialized_values, 
     187              collapse_queue_on_init: $collapse_queue_on_init 
    179188            }, 
    180189            use_query_string : false, 
  • plugins/sfWidgetFormInputSWFUploadPlugin/web/css/swfupload.css

    r19184 r19226  
    113113{ 
    114114  position: absolute; 
    115   bottom: 3px; 
     115  top: 3px; 
    116116  right: -19px; 
    117117  padding: 0; 
  • plugins/sfWidgetFormInputSWFUploadPlugin/web/js/swfupload-widget-handler.js

    r19190 r19226  
    6262var swfu_widget = 
    6363{ 
     64  HTTP_STATUS: { 
     65    100 : 'Continue', 
     66    101 : 'Switching Protocols', 
     67    200 : 'OK', 
     68    201 : 'Created', 
     69    202 : 'Accepted', 
     70    203 : 'Non-Authoritative Information', 
     71    204 : 'No Content', 
     72    205 : 'Reset Content', 
     73    206 : 'Partial Content', 
     74    300 : 'Multiple Choices', 
     75    301 : 'Moved Permanently', 
     76    302 : 'Found', 
     77    303 : 'See Other', 
     78    304 : 'Not Modified', 
     79    305 : 'Use Proxy', 
     80    306 : '(Unused)', 
     81    307 : 'Temporary Redirect', 
     82    400 : 'Bad Request', 
     83    401 : 'Unauthorized', 
     84    402 : 'Payment Required', 
     85    403 : 'Forbidden', 
     86    404 : 'Not Found', 
     87    405 : 'Method Not Allowed', 
     88    406 : 'Not Acceptable', 
     89    407 : 'Proxy Authentication Required', 
     90    408 : 'Request Timeout', 
     91    409 : 'Conflict', 
     92    410 : 'Gone', 
     93    411 : 'Length Required', 
     94    412 : 'Precondition Failed', 
     95    413 : 'Request Entity Too Large', 
     96    414 : 'Request-URI Too Long', 
     97    415 : 'Unsupported Media Type', 
     98    416 : 'Requested Range Not Satisfiable', 
     99    417 : 'Expectation Failed', 
     100    500 : 'Internal Server Error', 
     101    501 : 'Not Implemented', 
     102    502 : 'Bad Gateway', 
     103    503 : 'Service Unavailable', 
     104    504 : 'Gateway Timeout', 
     105    505 : 'HTTP Version Not Supported' 
     106  }, 
    64107  OS: function() 
    65108  { 
     
    198241  }, 
    199242 
     243  FormSerializer: function(form) 
     244  { 
     245    this.form = form; 
     246  }, 
     247   
    200248  handlers: 
    201249  { 
     
    360408    this._files = {}; 
    361409    this._swfupload = swfupload_instance; 
     410 
     411    this._postParams = typeof this._swfupload.settings.post_params === 'object' ? this._swfupload.settings.post_params : {}; 
     412     
     413    var el = this._swfupload.movieElement; 
     414    this._form = {}; 
     415    while(el = el.parentNode) 
     416    { 
     417      if(el.tagName.toLowerCase() === 'form') 
     418      { 
     419        this._form = el;break; 
     420      } 
     421    } 
     422    this._sendSerializedValues = this._swfupload.customSettings.send_serialized_values; 
     423 
    362424    this._widget = document.getElementById(this._swfupload.customSettings.widget_id); 
     425    this._collapseQueueOnInit = this._swfupload.customSettings.collapse_queue_on_init; 
    363426    this._message = '&nbsp;'; 
    364427    this._bytesLoaded = 0; 
     
    417480swfu_widget.mixin(swfu_widget.handlers, swfu_widget.observable); 
    418481 
     482swfu_widget.FormSerializer.prototype.serialize = function() 
     483{ 
     484  var form = this.form; 
     485  var els = {}; 
     486  loop:for(var i=0; len=form.elements.length, i<len; i++) 
     487  { 
     488    var el = form.elements[i]; 
     489    if( 
     490      !el.name || 
     491      (typeof el.disabled !== 'undefined' && el.disabled) || 
     492      (typeof el.checked !== 'undefined' && ['radio', 'checkbox'].indexOf(el.type.toLowerCase()) !== -1 && el.checked === false) || 
     493      (typeof el.selectedIndex !== 'undefined' && el.selectedIndex === -1) || 
     494      ['input', 'textarea', 'select'].indexOf(el.tagName.toLowerCase()) === -1 
     495    ) continue loop; 
     496     
     497    switch(el.tagName.toLowerCase()) 
     498    { 
     499      case 'input': 
     500        switch(el.type) 
     501        { 
     502          case 'hidden': 
     503          case 'text': 
     504          case 'password': 
     505            if(typeof els[el.name] === 'undefined') 
     506              els[el.name] = []; 
     507            els[el.name].push(el.value); 
     508            break; 
     509          case 'radio': 
     510          case 'checkbox': 
     511            if(typeof els[el.name] === 'undefined') 
     512              els[el.name] = []; 
     513            if(el.checked) 
     514              els[el.name].push(el.value); 
     515            break; 
     516        } 
     517        break; 
     518      case 'textarea': 
     519        if(typeof els[el.name] === 'undefined') 
     520          els[el.name] = []; 
     521        els[el.name].push(el.value); 
     522        break; 
     523      case 'select': 
     524        if(el.selectedIndex !== -1) 
     525        { 
     526          if(typeof els[el.name] === 'undefined') 
     527            els[el.name] = []; 
     528          for(var j=0; len2=el.options.length, j<len2; j++) 
     529          { 
     530            if(el.options[j].selected) 
     531            { 
     532              els[el.name].push(el.options[j].value); 
     533            } 
     534          } 
     535        } 
     536        break; 
     537    } 
     538  } 
     539  var cleaned_els = {}; 
     540  for(var i in els) 
     541  { 
     542    var el = els[i]; 
     543     
     544    if(i.lastIndexOf('[]') === i.length - 2) 
     545    { 
     546      var base_name = i.substring(0, i.lastIndexOf('[]')); 
     547       
     548      // reindex 
     549      for(var j=0; len=el.length, j<len; j++) 
     550      { 
     551        cleaned_els[base_name + "["+j+"]"] = el[j]; 
     552      } 
     553    } 
     554    else 
     555    { 
     556      cleaned_els[i] = el.pop().toString(); 
     557    } 
     558  } 
     559  return cleaned_els; 
     560} 
     561 
     562 
    419563// SWFUploadProgress 
    420564 
     
    472616  swfu_widget.addObserver(this, 'begin_upload', function(event) 
    473617  { 
     618    // serialize form values 
     619    if(this._sendSerializedValues) 
     620    { 
     621      var f = new swfu_widget.FormSerializer(this._form); 
     622      this.getSWFUpload().setUploadURL(this._form.action); 
     623      this.getSWFUpload().setPostParams(swfu_widget.mixin(this._postParams, f.serialize())); 
     624      this.getSWFUpload().addPostParam('swfupload_filesource', 'swf_upload'); 
     625    } 
    474626    this.setMessage('Upload in progress...'); 
    475627    this.getView().disableBrowseButton(); 
     
    749901    flash_object.style.top      = self._html.browseFilesLink.offsetTop + "px"; 
    750902  },0); 
     903 
     904  // Hide queue on init if set 
     905  if(this.getController()._collapseQueueOnInit) 
     906  { 
     907    this.toggleQueueDisplay(); 
     908  } 
    751909} 
    752910 
     
    10521210      { 
    10531211        case SWFUpload.UPLOAD_ERROR.HTTP_ERROR : 
    1054           this.setMessage('An HTTP error occured: ' + event.smessage); 
     1212          // message is status code 
     1213          this.setMessage('An HTTP error occured: ' + event.message + "&nbsp;" + swfu_widget.HTTP_STATUS[event.message]); 
    10551214          break; 
    10561215        case SWFUpload.UPLOAD_ERROR.MISSING_UPLOAD_URL : 
     
    10921251    if(event.file.id == this._file.id) 
    10931252    { 
    1094       this.setIsOk(true); 
    1095       this.setMessage('File uploaded'); 
     1253      // Server data or default message? 
     1254      if(event.server_data) 
     1255      { 
     1256        var json; 
     1257        try 
     1258        { 
     1259          json=JSON.parse(event.server_data); 
     1260           
     1261        } 
     1262        catch(e){json=false; } 
     1263 
     1264        if(json===false) 
     1265        { 
     1266          this.setIsOk(true); 
     1267          this.setMessage(event.server_data); 
     1268        } 
     1269        else 
     1270        { 
     1271          if(json.isError === true) 
     1272          { 
     1273            this.getSWFUploadProgress().setIsError(true); 
     1274            this.setIsError(true); 
     1275          } 
     1276          else 
     1277          { 
     1278            this.setIsOk(true); 
     1279          } 
     1280          this.setMessage(json.message); 
     1281        } 
     1282      } 
     1283      else 
     1284      { 
     1285        this.setIsOk(true); 
     1286        this.setMessage('File uploaded'); 
     1287      } 
    10961288      this.getProgressBar().setBytesLoaded(this._file.size); 
    10971289    } 
     
    14471639  this.setBytesLoaded(0); 
    14481640} 
     1641 
     1642/******************* JSON PARSER ***********************************************/ 
     1643/* 
     1644    http://www.JSON.org/json2.js 
     1645    2009-04-16 
     1646 
     1647    Public Domain. 
     1648 
     1649    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. 
     1650 
     1651    See http://www.JSON.org/js.html 
     1652 
     1653    This file creates a global JSON object containing two methods: stringify 
     1654    and parse. 
     1655 
     1656        JSON.stringify(value, replacer, space) 
     1657            value       any JavaScript value, usually an object or array. 
     1658 
     1659            replacer    an optional parameter that determines how object 
     1660                        values are stringified for objects. It can be a 
     1661                        function or an array of strings. 
     1662 
     1663            space       an optional parameter that specifies the indentation 
     1664                        of nested structures. If it is omitted, the text will 
     1665                        be packed without extra whitespace. If it is a number, 
     1666                        it will specify the number of spaces to indent at each 
     1667                        level. If it is a string (such as '\t' or '&nbsp;'), 
     1668                        it contains the characters used to indent at each level. 
     1669 
     1670            This method produces a JSON text from a JavaScript value. 
     1671 
     1672            When an object value is found, if the object contains a toJSON 
     1673            method, its toJSON method will be called and the result will be 
     1674            stringified. A toJSON method does not serialize: it returns the 
     1675            value represented by the name/value pair that should be serialized, 
     1676            or undefined if nothing should be serialized. The toJSON method 
     1677            will be passed the key associated with the value, and this will be 
     1678            bound to the object holding the key. 
     1679 
     1680            For example, this would serialize Dates as ISO strings. 
     1681 
     1682                Date.prototype.toJSON = function (key) { 
     1683                    function f(n) { 
     1684                        // Format integers to have at least two digits. 
     1685                        return n < 10 ? '0' + n : n; 
     1686                    } 
     1687 
     1688                    return this.getUTCFullYear()   + '-' + 
     1689                         f(this.getUTCMonth() + 1) + '-' + 
     1690                         f(this.getUTCDate())      + 'T' + 
     1691                         f(this.getUTCHours())     + ':' + 
     1692                         f(this.getUTCMinutes())   + ':' + 
     1693                         f(this.getUTCSeconds())   + 'Z'; 
     1694                }; 
     1695 
     1696            You can provide an optional replacer method. It will be passed the 
     1697            key and value of each member, with this bound to the containing 
     1698            object. The value that is returned from your method will be 
     1699            serialized. If your method returns undefined, then the member will 
     1700            be excluded from the serialization. 
     1701 
     1702            If the replacer parameter is an array of strings, then it will be 
     1703            used to select the members to be serialized. It filters the results 
     1704            such that only members with keys listed in the replacer array are 
     1705            stringified. 
     1706 
     1707            Values that do not have JSON representations, such as undefined or 
     1708            functions, will not be serialized. Such values in objects will be 
     1709            dropped; in arrays they will be replaced with null. You can use 
     1710            a replacer function to replace those with JSON values. 
     1711            JSON.stringify(undefined) returns undefined. 
     1712 
     1713            The optional space parameter produces a stringification of the 
     1714            value that is filled with line breaks and indentation to make it 
     1715            easier to read. 
     1716 
     1717            If the space parameter is a non-empty string, then that string will 
     1718            be used for indentation. If the space parameter is a number, then 
     1719            the indentation will be that many spaces. 
     1720 
     1721            Example: 
     1722 
     1723            text = JSON.stringify(['e', {pluribus: 'unum'}]); 
     1724            // text is '["e",{"pluribus":"unum"}]' 
     1725 
     1726 
     1727            text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); 
     1728            // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' 
     1729 
     1730            text = JSON.stringify([new Date()], function (key, value) { 
     1731                return this[key] instanceof Date ? 
     1732                    'Date(' + this[key] + ')' : value; 
     1733            }); 
     1734            // text is '["Date(---current time---)"]' 
     1735 
     1736 
     1737        JSON.parse(text, reviver) 
     1738            This method parses a JSON text to produce an object or array. 
     1739            It can throw a SyntaxError exception. 
     1740 
     1741            The optional reviver parameter is a function that can filter and 
     1742            transform the results. It receives each of the keys and values, 
     1743            and its return value is used instead of the original value. 
     1744            If it returns what it received, then the structure is not modified. 
     1745            If it returns undefined then the member is deleted. 
     1746 
     1747            Example: 
     1748 
     1749            // Parse the text. Values that look like ISO date strings will 
     1750            // be converted to Date objects. 
     1751 
     1752            myData = JSON.parse(text, function (key, value) { 
     1753                var a; 
     1754                if (typeof value === 'string') { 
     1755                    a = 
     1756/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); 
     1757                    if (a) { 
     1758                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], 
     1759                            +a[5], +a[6])); 
     1760                    } 
     1761                } 
     1762                return value; 
     1763            }); 
     1764 
     1765            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { 
     1766                var d; 
     1767                if (typeof value === 'string' && 
     1768                        value.slice(0, 5) === 'Date(' && 
     1769                        value.slice(-1) === ')') { 
     1770                    d = new Date(value.slice(5, -1)); 
     1771                    if (d) { 
     1772                        return d; 
     1773                    } 
     1774                } 
     1775                return value; 
     1776            }); 
     1777 
     1778 
     1779    This is a reference implementation. You are free to copy, modify, or 
     1780    redistribute. 
     1781 
     1782    This code should be minified before deployment. 
     1783    See http://javascript.crockford.com/jsmin.html 
     1784 
     1785    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO 
     1786    NOT CONTROL. 
     1787*/ 
     1788 
     1789/*jslint evil: true */ 
     1790 
     1791/*global JSON */ 
     1792 
     1793/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, 
     1794    call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, 
     1795    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, 
     1796    lastIndex, length, parse, prototype, push, replace, slice, stringify, 
     1797    test, toJSON, toString, valueOf 
     1798*/ 
     1799 
     1800// Create a JSON object only if one does not already exist. We create the 
     1801// methods in a closure to avoid creating global variables. 
     1802 
     1803if (!this.JSON) { 
     1804    JSON = {}; 
     1805} 
     1806(function () { 
     1807 
     1808    function f(n) { 
     1809        // Format integers to have at least two digits. 
     1810        return n < 10 ? '0' + n : n; 
     1811    } 
     1812 
     1813    if (typeof Date.prototype.toJSON !== 'function') { 
     1814 
     1815        Date.prototype.toJSON = function (key) { 
     1816 
     1817            return this.getUTCFullYear()   + '-' + 
     1818                 f(this.getUTCMonth() + 1) + '-' + 
     1819                 f(this.getUTCDate())      + 'T' + 
     1820                 f(this.getUTCHours())     + ':' + 
     1821                 f(this.getUTCMinutes())   + ':' + 
     1822                 f(this.getUTCSeconds())   + 'Z'; 
     1823        }; 
     1824 
     1825        String.prototype.toJSON = 
     1826        Number.prototype.toJSON = 
     1827        Boolean.prototype.toJSON = function (key) { 
     1828            return this.valueOf(); 
     1829        }; 
     1830    } 
     1831 
     1832    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 
     1833        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 
     1834        gap, 
     1835        indent, 
     1836        meta = {    // table of character substitutions 
     1837            '\b': '\\b', 
     1838            '\t': '\\t', 
     1839            '\n': '\\n', 
     1840            '\f': '\\f', 
     1841            '\r': '\\r', 
     1842            '"' : '\\"', 
     1843            '\\': '\\\\' 
     1844        }, 
     1845        rep; 
     1846 
     1847 
     1848    function quote(string) { 
     1849 
     1850// If the string contains no control characters, no quote characters, and no 
     1851// backslash characters, then we can safely slap some quotes around it. 
     1852// Otherwise we must also replace the offending characters with safe escape 
     1853// sequences. 
     1854 
     1855        escapable.lastIndex = 0; 
     1856        return escapable.test(string) ? 
     1857            '"' + string.replace(escapable, function (a) { 
     1858                var c = meta[a]; 
     1859                return typeof c === 'string' ? c : 
     1860                    '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); 
     1861            }) + '"' : 
     1862            '"' + string + '"'; 
     1863    } 
     1864 
     1865 
     1866    function str(key, holder) { 
     1867 
     1868// Produce a string from holder[key]. 
     1869 
     1870        var i,          // The loop counter. 
     1871            k,          // The member key. 
     1872            v,          // The member value. 
     1873            length, 
     1874            mind = gap, 
     1875            partial, 
     1876            value = holder[key]; 
     1877 
     1878// If the value has a toJSON method, call it to obtain a replacement value. 
     1879 
     1880        if (value && typeof value === 'object' && 
     1881                typeof value.toJSON === 'function') { 
     1882            value = value.toJSON(key); 
     1883        } 
     1884 
     1885// If we were called with a replacer function, then call the replacer to 
     1886// obtain a replacement value. 
     1887 
     1888        if (typeof rep === 'function') { 
     1889            value = rep.call(holder, key, value); 
     1890        } 
     1891 
     1892// What happens next depends on the value's type. 
     1893 
     1894        switch (typeof value) { 
     1895        case 'string': 
     1896            return quote(value); 
     1897 
     1898        case 'number': 
     1899 
     1900// JSON numbers must be finite. Encode non-finite numbers as null. 
     1901 
     1902            return isFinite(value) ? String(value) : 'null'; 
     1903 
     1904        case 'boolean': 
     1905        case 'null': 
     1906 
     1907// If the value is a boolean or null, convert it to a string. Note: 
     1908// typeof null does not produce 'null'. The case is included here in 
     1909// the remote chance that this gets fixed someday. 
     1910 
     1911            return String(value); 
     1912 
     1913// If the type is 'object', we might be dealing with an object or an array or 
     1914// null. 
     1915 
     1916        case 'object': 
     1917 
     1918// Due to a specification blunder in ECMAScript, typeof null is 'object', 
     1919// so watch out for that case. 
     1920 
     1921            if (!value) { 
     1922                return 'null'; 
     1923            } 
     1924 
     1925// Make an array to hold the partial results of stringifying this object value. 
     1926 
     1927            gap += indent; 
     1928            partial = []; 
     1929 
     1930// Is the value an array? 
     1931 
     1932            if (Object.prototype.toString.apply(value) === '[object Array]') { 
     1933 
     1934// The value is an array. Stringify every element. Use null as a placeholder 
     1935// for non-JSON values. 
     1936 
     1937                length = value.length; 
     1938                for (i = 0; i < length; i += 1) { 
     1939                    partial[i] = str(i, value) || 'null'; 
     1940                } 
     1941 
     1942// Join all of the elements together, separated with commas, and wrap them in 
     1943// brackets. 
     1944 
     1945                v = partial.length === 0 ? '[]' : 
     1946                    gap ? '[\n' + gap + 
     1947                            partial.join(',\n' + gap) + '\n' + 
     1948                                mind + ']' : 
     1949                          '[' + partial.join(',') + ']'; 
     1950                gap = mind; 
     1951                return v; 
     1952            } 
     1953 
     1954// If the replacer is an array, use it to select the members to be stringified. 
     1955 
     1956            if (rep && typeof rep === 'object') { 
     1957                length = rep.length; 
     1958                for (i = 0; i < length; i += 1) { 
     1959                    k = rep[i]; 
     1960                    if (typeof k === 'string') { 
     1961                        v = str(k, value); 
     1962                        if (v) { 
     1963                            partial.push(quote(k) + (gap ? ': ' : ':') + v); 
     1964                        } 
     1965                    } 
     1966                } 
     1967            } else { 
     1968 
     1969// Otherwise, iterate through all of the keys in the object. 
     1970 
     1971                for (k in value) { 
     1972                    if (Object.hasOwnProperty.call(value, k)) { 
     1973                        v = str(k, value); 
     1974                        if (v) { 
     1975                            partial.push(quote(k) + (gap ? ': ' : ':') + v); 
     1976                        } 
     1977                    } 
     1978                } 
     1979            } 
     1980 
     1981// Join all of the member texts together, separated with commas, 
     1982// and wrap them in braces. 
     1983 
     1984            v = partial.length === 0 ? '{}' : 
     1985                gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + 
     1986                        mind + '}' : '{' + partial.join(',') + '}'; 
     1987            gap = mind; 
     1988            return v; 
     1989        } 
     1990    } 
     1991 
     1992// If the JSON object does not yet have a stringify method, give it one. 
     1993 
     1994    if (typeof JSON.stringify !== 'function') { 
     1995        JSON.stringify = function (value, replacer, space) { 
     1996 
     1997// The stringify method takes a value and an optional replacer, and an optional 
     1998// space parameter, and returns a JSON text. The replacer can be a function 
     1999// that can replace values, or an array of strings that will select the keys. 
     2000// A default replacer method can be provided. Use of the space parameter can 
     2001// produce text that is more easily readable. 
     2002 
     2003            var i; 
     2004            gap = ''; 
     2005            indent = ''; 
     2006 
     2007// If the space parameter is a number, make an indent string containing that 
     2008// many spaces. 
     2009 
     2010            if (typeof space === 'number') { 
     2011                for (i = 0; i < space; i += 1) { 
     2012                    indent += ' '; 
     2013                } 
     2014 
     2015// If the space parameter is a string, it will be used as the indent string. 
     2016 
     2017            } else if (typeof space === 'string') { 
     2018                indent = space; 
     2019            } 
     2020 
     2021// If there is a replacer, it must be a function or an array. 
     2022// Otherwise, throw an error. 
     2023 
     2024            rep = replacer; 
     2025            if (replacer && typeof replacer !== 'function' && 
     2026                    (typeof replacer !== 'object' || 
     2027                     typeof replacer.length !== 'number')) { 
     2028                throw new Error('JSON.stringify'); 
     2029            } 
     2030 
     2031// Make a fake root object containing our value under the key of ''. 
     2032// Return the result of stringifying the value. 
     2033 
     2034            return str('', {'': value}); 
     2035        }; 
     2036    } 
     2037 
     2038 
     2039// If the JSON object does not yet have a parse method, give it one. 
     2040 
     2041    if (typeof JSON.parse !== 'function') { 
     2042        JSON.parse = function (text, reviver) { 
     2043 
     2044// The parse method takes a text and an optional reviver function, and returns 
     2045// a JavaScript value if the text is a valid JSON text. 
     2046 
     2047            var j; 
     2048 
     2049            function walk(holder, key) { 
     2050 
     2051// The walk method is used to recursively walk the resulting structure so 
     2052// that modifications can be made. 
     2053 
     2054                var k, v, value = holder[key]; 
     2055                if (value && typeof value === 'object') { 
     2056                    for (k in value) { 
     2057                        if (Object.hasOwnProperty.call(value, k)) { 
     2058                            v = walk(value, k); 
     2059                            if (v !== undefined) { 
     2060                                value[k] = v; 
     2061                            } else { 
     2062                                delete value[k]; 
     2063                            } 
     2064                        } 
     2065                    } 
     2066                } 
     2067                return reviver.call(holder, key, value); 
     2068            } 
     2069 
     2070 
     2071// Parsing happens in four stages. In the first stage, we replace certain 
     2072// Unicode characters with escape sequences. JavaScript handles many characters 
     2073// incorrectly, either silently deleting them, or treating them as line endings. 
     2074 
     2075            cx.lastIndex = 0; 
     2076            if (cx.test(text)) { 
     2077                text = text.replace(cx, function (a) { 
     2078                    return '\\u' + 
     2079                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4); 
     2080                }); 
     2081            } 
     2082 
     2083// In the second stage, we run the text against regular expressions that look 
     2084// for non-JSON patterns. We are especially concerned with '()' and 'new' 
     2085// because they can cause invocation, and '=' because it can cause mutation. 
     2086// But just to be safe, we want to reject all unexpected forms. 
     2087 
     2088// We split the second stage into 4 regexp operations in order to work around 
     2089// crippling inefficiencies in IE's and Safari's regexp engines. First we 
     2090// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we 
     2091// replace all simple value tokens with ']' characters. Third, we delete all 
     2092// open brackets that follow a colon or comma or that begin the text. Finally, 
     2093// we look to see that the remaining characters are only whitespace or ']' or 
     2094// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. 
     2095 
     2096            if (/^[\],:{}\s]*$/. 
     2097test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@'). 
     2098replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']'). 
     2099replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { 
     2100 
     2101// In the third stage we use the eval function to compile the text into a 
     2102// JavaScript structure. The '{' operator is subject to a syntactic ambiguity 
     2103// in JavaScript: it can begin a block or an object literal. We wrap the text 
     2104// in parens to eliminate the ambiguity. 
     2105 
     2106                j = eval('(' + text + ')'); 
     2107 
     2108// In the optional fourth stage, we recursively walk the new structure, passing 
     2109// each name/value pair to a reviver function for possible transformation. 
     2110 
     2111                return typeof reviver === 'function' ? 
     2112                    walk({'': j}, '') : j; 
     2113            } 
     2114 
     2115// If the text is not JSON parseable, then a SyntaxError is thrown. 
     2116 
     2117            throw new SyntaxError('JSON.parse'); 
     2118        }; 
     2119    } 
     2120}());