diff options
| -rw-r--r-- | NOTICE | 4 | ||||
| -rw-r--r-- | share/Makefile.am | 1 | ||||
| -rw-r--r-- | share/server/mimeparse.js | 158 | ||||
| -rw-r--r-- | share/server/render.js | 102 | 
4 files changed, 163 insertions, 102 deletions
| @@ -29,3 +29,7 @@ This product also includes the following third-party components:   * ibrowse (http://github.com/cmullaparthi/ibrowse/tree/master)     Copyright 2009, Chandrashekhar Mullaparthi + + * mimeparse.js (http://code.google.com/p/mimeparse/) + +   Copyright 2009, Chris Anderson <jchris@apache.org> diff --git a/share/Makefile.am b/share/Makefile.am index 8071525f..b17dcb4e 100644 --- a/share/Makefile.am +++ b/share/Makefile.am @@ -14,6 +14,7 @@ JS_FILE = server/main.js  JS_FILE_COMPONENTS = \      server/filter.js \ +    server/mimeparse.js \      server/render.js \      server/state.js \      server/util.js \ diff --git a/share/server/mimeparse.js b/share/server/mimeparse.js new file mode 100644 index 00000000..3642a194 --- /dev/null +++ b/share/server/mimeparse.js @@ -0,0 +1,158 @@ +// mimeparse.js +// +// This module provides basic functions for handling mime-types. It can +// handle matching mime-types against a list of media-ranges. See section +// 14.1 of the HTTP specification [RFC 2616] for a complete explanation. +// +//   http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1 +// +// A port to JavaScript of Joe Gregorio's MIME-Type Parser: +// +//   http://code.google.com/p/mimeparse/ +// +// Ported by J. Chris Anderson <jchris@apache.org>, targeting the Spidermonkey runtime. +// +// To run the tests, open mimeparse-js-test.html in a browser. +// Ported from version 0.1.2 +// Comments are mostly excerpted from the original. + +var Mimeparse = (function() { +  // private helpers +  function strip(string) { +    return string.replace(/^\s+/, '').replace(/\s+$/, '') +  }; + +  function parseRanges(ranges) { +    var parsedRanges = [], rangeParts = ranges.split(","); +    for (var i=0; i < rangeParts.length; i++) { +      parsedRanges.push(publicMethods.parseMediaRange(rangeParts[i])) +    }; +    return parsedRanges; +  }; + +  var publicMethods = { +    // Carves up a mime-type and returns an Array of the +    //  [type, subtype, params] where "params" is a Hash of all +    //  the parameters for the media range. +    // +    // For example, the media range "application/xhtml;q=0.5" would +    //  get parsed into: +    // +    // ["application", "xhtml", { "q" : "0.5" }] +    parseMimeType : function(mimeType) { +      var fullType, typeParts, params = {}, parts = mimeType.split(';'); +      for (var i=0; i < parts.length; i++) { +        var p = parts[i].split('='); +        if (p.length == 2) { +          params[strip(p[0])] = strip(p[1]); +        } +      }; +      fullType = parts[0].replace(/^\s+/, '').replace(/\s+$/, ''); +      if (fullType == '*') fullType = '*/*'; +      typeParts = fullType.split('/'); +      return [typeParts[0], typeParts[1], params]; +    }, + +    // Carves up a media range and returns an Array of the +    //  [type, subtype, params] where "params" is a Object with +    //  all the parameters for the media range. +    // +    // For example, the media range "application/*;q=0.5" would +    //  get parsed into: +    // +    // ["application", "*", { "q" : "0.5" }] +    // +    // In addition this function also guarantees that there +    //  is a value for "q" in the params dictionary, filling it +    //  in with a proper default if necessary. +    parseMediaRange : function(range) { +      var q, parsedType = this.parseMimeType(range); +      if (!parsedType[2]['q']) { +        parsedType[2]['q'] = '1'; +      } else { +        q = parseFloat(parsedType[2]['q']); +        if (isNaN(q)) { +          parsedType[2]['q'] = '1'; +        } else if (q > 1 || q < 0) { +          parsedType[2]['q'] = '1'; +        } +      } +      return parsedType; +    }, + +    // Find the best match for a given mime-type against +    // a list of media_ranges that have already been +    // parsed by parseMediaRange(). Returns an array of +    // the fitness value and the value of the 'q' quality +    // parameter of the best match, or (-1, 0) if no match +    // was found. Just as for qualityParsed(), 'parsed_ranges' +    // must be a list of parsed media ranges. +    fitnessAndQualityParsed : function(mimeType, parsedRanges) { +      var bestFitness = -1, bestFitQ = 0, target = this.parseMediaRange(mimeType); +      var targetType = target[0], targetSubtype = target[1], targetParams = target[2]; + +      for (var i=0; i < parsedRanges.length; i++) { +        var parsed = parsedRanges[i]; +        var type = parsed[0], subtype = parsed[1], params = parsed[2]; +        if ((type == targetType || type == "*" || targetType == "*") && +          (subtype == targetSubtype || subtype == "*" || targetSubtype == "*")) { +          var matchCount = 0; +          for (param in targetParams) { +            if (param != 'q' && params[param] && params[param] == targetParams[param]) { +              matchCount += 1; +            } +          } + +          var fitness = (type == targetType) ? 100 : 0; +          fitness += (subtype == targetSubtype) ? 10 : 0; +          fitness += matchCount; + +          if (fitness > bestFitness) { +            bestFitness = fitness; +            bestFitQ = params["q"]; +          } +        } +      }; +      return [bestFitness, parseFloat(bestFitQ)]; +    }, + +    // Find the best match for a given mime-type against +    // a list of media_ranges that have already been +    // parsed by parseMediaRange(). Returns the +    // 'q' quality parameter of the best match, 0 if no +    // match was found. This function bahaves the same as quality() +    // except that 'parsedRanges' must be a list of +    // parsed media ranges. +    qualityParsed : function(mimeType, parsedRanges) { +      return this.fitnessAndQualityParsed(mimeType, parsedRanges)[1]; +    }, + +    // Returns the quality 'q' of a mime-type when compared +    // against the media-ranges in ranges. For example: +    // +    // >>> Mimeparse.quality('text/html','text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5') +    // 0.7 +    quality : function(mimeType, ranges) { +      return this.qualityParsed(mimeType, parseRanges(ranges)); +    }, + +    // Takes a list of supported mime-types and finds the best +    // match for all the media-ranges listed in header. The value of +    // header must be a string that conforms to the format of the +    // HTTP Accept: header. The value of 'supported' is a list of +    // mime-types. +    // +    // >>> bestMatch(['application/xbel+xml', 'text/xml'], 'text/*;q=0.5,*/*; q=0.1') +    // 'text/xml' +    bestMatch : function(supported, header) { +      var parsedHeader = parseRanges(header); +      var weighted = []; +      for (var i=0; i < supported.length; i++) { +        weighted.push([publicMethods.fitnessAndQualityParsed(supported[i], parsedHeader), i, supported[i]]) +      }; +      weighted.sort(); +      return weighted[weighted.length-1][0][1] ? weighted[weighted.length-1][2] : ''; +    } +  } +  return publicMethods; +})(); diff --git a/share/server/render.js b/share/server/render.js index 6efc9687..9537cd54 100644 --- a/share/server/render.js +++ b/share/server/render.js @@ -10,108 +10,6 @@  // License for the specific language governing permissions and limitations under  // the License. -// mimeparse.js -// http://code.google.com/p/mimeparse/ -// MIT Licensed http://www.opensource.org/licenses/mit-license.php -// Code with comments: http://mimeparse.googlecode.com/svn/trunk/mimeparse.js -// Tests: http://mimeparse.googlecode.com/svn/trunk/mimeparse-js-test.html -// Ported by Chris Anderson from version 0.1.2 - -var Mimeparse = (function() { -  function strip(string) { -    return string.replace(/^\s+/, '').replace(/\s+$/, ''); -  }; -  function parseRanges(ranges) { -    var parsedRanges = [], rangeParts = ranges.split(","); -    for (var i=0; i < rangeParts.length; i++) { -      parsedRanges.push(publicMethods.parseMediaRange(rangeParts[i])); -    }; -    return parsedRanges; -  }; -  var publicMethods = { -    parseMimeType : function(mimeType) { -      var fullType, typeParts, params = {}, parts = mimeType.split(';'); -      for (var i=0; i < parts.length; i++) { -        var p = parts[i].split('='); -        if (p.length == 2) { -          params[strip(p[0])] = strip(p[1]); -        } -      }; -      fullType = parts[0].replace(/^\s+/, '').replace(/\s+$/, ''); -      if (fullType == '*') fullType = '*/*'; -      typeParts = fullType.split('/'); -      return [typeParts[0], typeParts[1], params]; -    }, -    parseMediaRange : function(range) { -      var q, parsedType = this.parseMimeType(range); -      if (!parsedType[2]['q']) { -        parsedType[2]['q'] = '1'; -      } else { -        q = parseFloat(parsedType[2]['q']); -        if (isNaN(q)) { -          parsedType[2]['q'] = '1'; -        } else if (q > 1 || q < 0) { -          parsedType[2]['q'] = '1'; -        } -      } -      return parsedType; -    }, -    fitnessAndQualityParsed : function(mimeType, parsedRanges) { -      var bestFitness = -1, bestFitQ = 0, target = this.parseMediaRange(mimeType); -      var targetType = target[0], targetSubtype = target[1], targetParams = target[2]; - -      for (var i=0; i < parsedRanges.length; i++) { -        var parsed = parsedRanges[i]; -        var type = parsed[0], subtype = parsed[1], params = parsed[2]; -        if ((type == targetType || type == "*" || targetType == "*") && -          (subtype == targetSubtype || subtype == "*" || targetSubtype == "*")) { -          var matchCount = 0; -          for (param in targetParams) { -            if (param != 'q' && params[param] && params[param] == targetParams[param]) { -              matchCount += 1; -            } -          } - -          var fitness = (type == targetType) ? 100 : 0; -          fitness += (subtype == targetSubtype) ? 10 : 0; -          fitness += matchCount; - -          if (fitness > bestFitness) { -            bestFitness = fitness; -            bestFitQ = params["q"]; -          } -        } -      }; -      return [bestFitness, parseFloat(bestFitQ)]; -    }, -    qualityParsed : function(mimeType, parsedRanges) { -      return this.fitnessAndQualityParsed(mimeType, parsedRanges)[1]; -    }, -    quality : function(mimeType, ranges) { -      return this.qualityParsed(mimeType, parseRanges(ranges)); -    }, - -    // Takes a list of supported mime-types and finds the best -    // match for all the media-ranges listed in header. The value of -    // header must be a string that conforms to the format of the -    // HTTP Accept: header. The value of 'supported' is a list of -    // mime-types. -    // -    // >>> bestMatch(['application/xbel+xml', 'text/xml'], 'text/*;q=0.5,*/*; q=0.1') -    // 'text/xml' -    bestMatch : function(supported, header) { -      var parsedHeader = parseRanges(header); -      var weighted = []; -      for (var i=0; i < supported.length; i++) { -        weighted.push([publicMethods.fitnessAndQualityParsed(supported[i], parsedHeader), supported[i]]); -      }; -      weighted.sort(); -      return weighted[weighted.length-1][0][1] ? weighted[weighted.length-1][1] : ''; -    } -  }; -  return publicMethods; -})(); -  var respCT;  var respTail;  // this function provides a shortcut for managing responses by Accept header | 
