text: /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
   * full list of contributors). Published under the 2-clause BSD license.
   * See license.txt in the OpenLayers distribution or repository for the
   * full text of the license. */
  
  /**
   * @requires OpenLayers/Protocol.js
   * @requires OpenLayers/Feature/Vector.js
   * @requires OpenLayers/Format/GeoJSON.js
   */
  
  /**
   * if application uses the query string, for example, for BBOX parameters,
   * OpenLayers/Format/QueryStringFilter.js should be included in the build config file
   */
  
  /**
   * Class: OpenLayers.Protocol.Script
   * A basic Script protocol for vector layers.  Create a new instance with the
   *     <OpenLayers.Protocol.Script> constructor.  A script protocol is used to
   *     get around the same origin policy.  It works with services that return
   *     JSONP - that is, JSON wrapped in a client-specified callback.  The
   *     protocol handles fetching and parsing of feature data and sends parsed
   *     features to the <callback> configured with the protocol.  The protocol
   *     expects features serialized as GeoJSON by default, but can be configured
   *     to work with other formats by setting the <format> property.
   *
   * Inherits from:
   *  - <OpenLayers.Protocol>
   */
  OpenLayers.Protocol.Script = OpenLayers.Class(OpenLayers.Protocol, {
  
      /**
       * APIProperty: url
       * {String} Service URL.  The service is expected to return serialized 
       *     features wrapped in a named callback (where the callback name is
       *     generated by this protocol).
       *     Read-only, set through the options passed to the constructor.
       */
      url: null,
  
      /**
       * APIProperty: params
       * {Object} Query string parameters to be appended to the URL.
       *     Read-only, set through the options passed to the constructor.
       *     Example: {maxFeatures: 50}
       */
      params: null,
      
      /**
       * APIProperty: callback
       * {Object} Function to be called when the <read> operation completes.
       */
      callback: null,
  
      /**
       * APIProperty: callbackTemplate
       * {String} Template for creating a unique callback function name
       * for the registry. Should include ${id}.  The ${id} variable will be
       * replaced with a string identifier prefixed with a "c" (e.g. c1, c2).
       * Default is "OpenLayers.Protocol.Script.registry.${id}".
       */
      callbackTemplate: "OpenLayers.Protocol.Script.registry.${id}",
  
      /**
       * APIProperty: callbackKey
       * {String} The name of the query string parameter that the service 
       *     recognizes as the callback identifier.  Default is "callback".
       *     This key is used to generate the URL for the script.  For example
       *     setting <callbackKey> to "myCallback" would result in a URL like 
       *     http://example.com/?myCallback=...
       */
      callbackKey: "callback",
  
      /**
       * APIProperty: callbackPrefix
       * {String} Where a service requires that the callback query string 
       *     parameter value is prefixed by some string, this value may be set.
       *     For example, setting <callbackPrefix> to "foo:" would result in a
       *     URL like http://example.com/?callback=foo:...  Default is "".
       */
      callbackPrefix: "",
  
      /**
       * APIProperty: scope
       * {Object} Optional ``this`` object for the callback. Read-only, set 
       *     through the options passed to the constructor.
       */
      scope: null,
  
      /**
       * APIProperty: format
       * {<OpenLayers.Format>} Format for parsing features.  Default is an 
       *     <OpenLayers.Format.GeoJSON> format.  If an alternative is provided,
       *     the format's read method must take an object and return an array
       *     of features.
       */
      format: null,
  
      /**
       * Property: pendingRequests
       * {Object} References all pending requests.  Property names are script 
       *     identifiers and property values are script elements.
       */
      pendingRequests: null,
  
      /**
       * APIProperty: srsInBBOX
       * {Boolean} Include the SRS identifier in BBOX query string parameter.
       *     Setting this property has no effect if a custom filterToParams method
       *     is provided.   Default is false.  If true and the layer has a 
       *     projection object set, any BBOX filter will be serialized with a 
       *     fifth item identifying the projection.  
       *     E.g. bbox=-1000,-1000,1000,1000,EPSG:900913
       */
      srsInBBOX: false,
  
      /**
       * Constructor: OpenLayers.Protocol.Script
       * A class for giving layers generic Script protocol.
       *
       * Parameters:
       * options - {Object} Optional object whose properties will be set on the
       *     instance.
       *
       * Valid options include:
       * url - {String}
       * params - {Object}
       * callback - {Function}
       * scope - {Object}
       */
      initialize: function(options) {
          options = options || {};
          this.params = {};
          this.pendingRequests = {};
          OpenLayers.Protocol.prototype.initialize.apply(this, arguments);
          if (!this.format) {
              this.format = new OpenLayers.Format.GeoJSON();
          }
  
          if (!this.filterToParams && OpenLayers.Format.QueryStringFilter) {
              var format = new OpenLayers.Format.QueryStringFilter({
                  srsInBBOX: this.srsInBBOX
              });
              this.filterToParams = function(filter, params) {
                  return format.write(filter, params);
              };
          }
      },
      
      /**
       * APIMethod: read
       * Construct a request for reading new features.
       *
       * Parameters:
       * options - {Object} Optional object for configuring the request.
       *     This object is modified and should not be reused.
       *
       * Valid options:
       * url - {String} Url for the request.
       * params - {Object} Parameters to get serialized as a query string.
       * filter - {<OpenLayers.Filter>} Filter to get serialized as a
       *     query string.
       *
       * Returns:
       * {<OpenLayers.Protocol.Response>} A response object, whose "priv" property
       *     references the injected script.  This object is also passed to the
       *     callback function when the request completes, its "features" property
       *     is then populated with the features received from the server.
       */
      read: function(options) {
          OpenLayers.Protocol.prototype.read.apply(this, arguments);
          options = OpenLayers.Util.applyDefaults(options, this.options);
          options.params = OpenLayers.Util.applyDefaults(
              options.params, this.options.params
          );
          if (options.filter && this.filterToParams) {
              options.params = this.filterToParams(
                  options.filter, options.params
              );
          }
          var response = new OpenLayers.Protocol.Response({requestType: "read"});
          var request = this.createRequest(
              options.url, 
              options.params, 
              OpenLayers.Function.bind(function(data) {
                  response.data = data;
                  this.handleRead(response, options);
              }, this)
          );
          response.priv = request;
          return response;
      },
  
      /** 
       * APIMethod: filterToParams 
       * Optional method to translate an <OpenLayers.Filter> object into an object 
       *     that can be serialized as request query string provided.  If a custom 
       *     method is not provided, any filter will not be serialized. 
       * 
       * Parameters: 
       * filter - {<OpenLayers.Filter>} filter to convert. 
       * params - {Object} The parameters object. 
       * 
       * Returns: 
       * {Object} The resulting parameters object. 
       */
  
      /** 
       * Method: createRequest
       * Issues a request for features by creating injecting a script in the 
       *     document head.
       *
       * Parameters:
       * url - {String} Service URL.
       * params - {Object} Query string parameters.
       * callback - {Function} Callback to be called with resulting data.
       *
       * Returns:
       * {HTMLScriptElement} The script pending execution.
       */
      createRequest: function(url, params, callback) {
          var id = OpenLayers.Protocol.Script.register(callback);
          var name = OpenLayers.String.format(this.callbackTemplate, {id: id});
          params = OpenLayers.Util.extend({}, params);
          params[this.callbackKey] = this.callbackPrefix + name;
          url = OpenLayers.Util.urlAppend(
              url, OpenLayers.Util.getParameterString(params)
          );
          var script = document.createElement("script");
          script.type = "text/javascript";
          script.src = url;
          script.id = "OpenLayers_Protocol_Script_" + id;
          this.pendingRequests[script.id] = script;
          var head = document.getElementsByTagName("head")[0];
          head.appendChild(script);
          return script;
      },
      
      /** 
       * Method: destroyRequest
       * Remove a script node associated with a response from the document.  Also
       *     unregisters the callback and removes the script from the 
       *     <pendingRequests> object.
       *
       * Parameters:
       * script - {HTMLScriptElement}
       */
      destroyRequest: function(script) {
          OpenLayers.Protocol.Script.unregister(script.id.split("_").pop());
          delete this.pendingRequests[script.id];
          if (script.parentNode) {
              script.parentNode.removeChild(script);
          }
      },
  
      /**
       * Method: handleRead
       * Individual callbacks are created for read, create and update, should
       *     a subclass need to override each one separately.
       *
       * Parameters:
       * response - {<OpenLayers.Protocol.Response>} The response object to pass to
       *     the user callback.
       * options - {Object} The user options passed to the read call.
       */
      handleRead: function(response, options) {
          this.handleResponse(response, options);
      },
  
      /**
       * Method: handleResponse
       * Called by CRUD specific handlers.
       *
       * Parameters:
       * response - {<OpenLayers.Protocol.Response>} The response object to pass to
       *     any user callback.
       * options - {Object} The user options passed to the create, read, update,
       *     or delete call.
       */
      handleResponse: function(response, options) {
          if (options.callback) {
              if (response.data) {
                  response.features = this.parseFeatures(response.data);
                  response.code = OpenLayers.Protocol.Response.SUCCESS;
              } else {
                  response.code = OpenLayers.Protocol.Response.FAILURE;
              }
              this.destroyRequest(response.priv);
              options.callback.call(options.scope, response);
          }
      },
  
      /**
       * Method: parseFeatures
       * Read Script response body and return features.
       *
       * Parameters:
       * data - {Object} The data sent to the callback function by the server.
       *
       * Returns:
       * {Array({<OpenLayers.Feature.Vector>})} or
       *     {<OpenLayers.Feature.Vector>} Array of features or a single feature.
       */
      parseFeatures: function(data) {
          return this.format.read(data);
      },
  
      /**
       * APIMethod: abort
       * Abort an ongoing request.  If no response is provided, all pending 
       *     requests will be aborted.
       *
       * Parameters:
       * response - {<OpenLayers.Protocol.Response>} The response object returned
       *     from a <read> request.
       */
      abort: function(response) {
          if (response) {
              this.destroyRequest(response.priv);
          } else {
              for (var key in this.pendingRequests) {
                  this.destroyRequest(this.pendingRequests[key]);
              }
          }
      },
      
      /**
       * APIMethod: destroy
       * Clean up the protocol.
       */
      destroy: function() {
          this.abort();
          delete this.params;
          delete this.format;
          OpenLayers.Protocol.prototype.destroy.apply(this);
      },
  
      CLASS_NAME: "OpenLayers.Protocol.Script" 
  });
  
  (function() {
      var o = OpenLayers.Protocol.Script;
      var counter = 0;
      o.registry = {};
      
      /**
       * Function: OpenLayers.Protocol.Script.register
       * Register a callback for a newly created script.
       *
       * Parameters:
       * callback - {Function} The callback to be executed when the newly added
       *     script loads.  This callback will be called with a single argument
       *     that is the JSON returned by the service.
       *
       * Returns:
       * {Number} An identifier for retrieving the registered callback.
       */
      o.register = function(callback) {
          var id = "c"+(++counter);
          o.registry[id] = function() {
              callback.apply(this, arguments);
          };
          return id;
      };
      
      /**
       * Function: OpenLayers.Protocol.Script.unregister
       * Unregister a callback previously registered with the register function.
       *
       * Parameters:
       * id - {Number} The identifer returned by the register function.
       */
      o.unregister = function(id) {
          delete o.registry[id];
      };
  })();
  