<commit_before>(function() {

/**
 * create root namespace for all twentyc definitions
 * @module twentyc
 */

twentyc = {};

/**
 * class helper functions
 * @module twentyc
 * @class cls
 * @static
 */

twentyc.cls = {

  /**
   * converts a string into a standardized class name, replacing
   * invalid characters with valid ones.
   *
   *     twentyc.cls.make_name("class-a"); // classA
   *     twentyc.cls.make_name("class a b"); // classAB
   *     twentyc.cls.make_name("Class-A_B"); // ClassA_B
   *
   * @method make_name
   * @param {String} name base name
   * @returns {String} class_name changed name
   */

  make_name : function(name) {
    var i, names = name.split(/[-\s]/);
    for(i = 0; i < names.length; i++) {
      if(i > 0)
        names[i] = names[i].charAt(0).toUpperCase() + names[i].slice(1);
    }
    return names.join("");
  },

  /**
   * create a new class - if you wish to extend a class take a look at
   * {{#crossLink "cls/extend:method"}}twentyc.cls.extend{{/crossLink}}
   * instead
   *
   * you may define a constructor in the definition by using the class
   * name you provide at __name__
   *
   * Note that if the name you provide is not a valid variable name
   * it will be passed through twentyc.cls.make_name to make it valid
   *
   * ##examples
   *
   * * define and instantiate a class: examples/cls.define.js
   *
   * @method define
   * @param {String} name name or unique identifier of the new class
   * @param {Object} definition object literal of properties and functions that you wish define or redefine
   * @returns {Function} dest constructor of new class
   */

  define : function(name, definition) {

    var k;

    name = twentyc.cls.make_name(name);

    if(typeof(definition[name]) == "function") {
      // a constructor has been provided
      var ctor = definition[name]
      delete definition[name]
    } else {
      // no constructor provided, substitute empty constructor
      var ctor = function(){};
    }

    // cycle through definition and copy to class prototype
    for(k in definition) {
      ctor.prototype[k] = definition[k]
    }

    // create meta information
    ctor.prototype._meta = {
      "name" : name
    }

    return ctor

  },

  /**
   * extend an existing class with new properties and functions
   *
   * if a function is defined that already exists on the parent class
   * this function will be overwritten and a reference to the original
   * function will be provided at parentClassName_functionName
   *
   * you may define a constructor in the definition by using the class
   * name you provide at __name__
   *
   * Note that if the name you provide is not a valid variable name
   * it will be passed through twentyc.cls.make_name to make it valid
   *
   * ##examples
   *
   * * extend and instantiate a class: examples/cls.extend.js
   * * handling method override: examples/cls.extend.method-override.js
   *
   * @method extend
   * @param {String} name name or unqiue identifier of the new class
   * @param {Object} definition object literal of properties and functions that you wish define or redefine
   * @param {Function} [parent] constructor of class
   * that you wish to extend, if omitted an empty function will be substituted
   * @returns {Function} dest constructor of new class
   */

  extend : function(name, definition, parent) {

    var k;
    name = twentyc.cls.make_name(name);

    if(typeof(definition[name]) == "function") {
      // a constructor has been provided
      var ctor = definition[name]
      delete definition[name]
    } else {
      // no constructor provided, substitute empty constructor
      var ctor = function(){
        parent.apply(this, arguments)
      };
    }

    // cycle through parent prototype and copy to class prototype
    for(k in parent.prototype) {
      ctor.prototype[k] = parent.prototype[k]
    }

    // cycle through definition and copy to class prototype
    for(k in definition) {
      if(typeof(ctor.prototype[k]) == "function") {
        // function was already defined by parent, store backref
        ctor.prototype[parent.prototype._meta.name+"_"+k] = parent.prototype[k];
      }
      ctor.prototype[k] = definition[k]
    }

    // reference parent constructor
    ctor.prototype[parent.prototype._meta.name] = parent

    // create meta information
    ctor.prototype._meta = {
      "name" : name,
      "parent" : parent
    }

    return ctor

  },

  /**
   * overrides a method on the provided class
   *
   * @method override
   * @param {Function} destClass A class created via __twentyc.cls.define__ or __twentyc.cls.extend__
   * @param {String} methodName name of method that you wish to override
   * @param {Function} method new method
   */

  override : function(destClass, methodName, method) {

    // create reference to old method
    if(destClass.prototype[methodName])
      destClass.prototype[destClass.prototype._meta.name+"_"+methodName] = destClass.prototype[methodName];

    // override
    destClass.prototype[methodName] = method;

  }

}

/**
 * class registry object - allows you to quickly define and extend
 * similar classes
 *
 * @module twentyc
 * @namespace twentyc.cls
 * @class Registry
 * @constructor
 */

twentyc.cls.Registry = twentyc.cls.define(
  "Registry",
  {

    Registry : function() {

      /**
       * holds the classes defined in this registry
       * @property _classes
       * @type Object
       * @private
       */

      this._classes = {}

    },

    /**
     * register a new class - look at twentyc.cls.define and twentyc.cls.extend for param
     * explanation
     * @method register
     * @param {String} name class name
     * @param {Object} definition class definition
     * @param {String} extend if passed, extend this class from this class (needs to exist in the Registry)
     * @returns {Function} class ctor of the newly created class
     */

     register : function(name, definition, extend) {
       if(this._classes[name] != undefined) {
         throw("Class with name '"+name+"' already exists - name must be unique in the Registry");
       }
       if(extend && typeof this._classes[extend] != "function") {
         throw("Trying to extend from class unknown to this Registry: "+extend);
       }
       if(extend)
         this._classes[name] = twentyc.cls.extend(name, definition, this._classes[extend]);
       else
         this._classes[name] = twentyc.cls.define(name, definition);
       return this._classes[name];
     },

     /**
      * get a registered class constructor
      * @method get
      * @param {String} name class name
      * @returns {Function} class ctor of registered class
      */

     get : function(name) {
       if(typeof this._classes[name] != "function")
         throw("Trying to retrieve class unknown to this Registry: "+name);
       return this._classes[name];
     }
  }
);

/**
 * utility functions
 * @module twentyc
 * @class util
 * @static
 */

twentyc.util = {

  /**
   * retrieve value from object literal - allows you to pass null or undefined
   * as the object and will return null if you do
   *
   * @method get
   * @param {Object} obj
   * @param {String) key
   * @param {Mixed} [default] return this if obj is null or key does not exist
   * @returns {Mixed} value
   */

  get : function(obj, key, dflt) {
    if(!obj || obj[key] == undefined)
      return dflt;
    return obj[key]
  }

}

/**
 * data retrieval, storage and management
 *
 * assures that data is only retrieved once even when multiple sources
 * are requesting it.
 *
 * data is cached locally for quick retrieval afterwards
 *
 * @module twentyc
 * @class data
 * @static
 */

twentyc.data = {

  /**
   * fires everytime a dataset is retrieved from the server - __*__ is
   * substituted with the data id
   *
   * all handlers bound to this event will be removed after its been
   * triggered - in order to permanently subscribe a load event
   * subscribe to "load"
   *
   * @event load-*
   * @param {String} id data id
   * @param {Object} data object literal of retrieved data
   */

  /**
   * fires everytime a dataset is retrieved from the server
   *
   * @event load
   * @param {String} id data id
   * @param {Object} data object literal of retrieved data
   */

  /**
   * keeps track of current loading status
   * @property _loading
   * @type Object
   * @private
   */

  _loading : {},

  /**
   * _data storage, keyed by data id
   * @property data
   * @type Object
   * @private
   */

  _data : {},

  /**
   * attempts to retrieve and return a data set
   *
   * ##example(s)
   *
   * examples/data.load.js
   *
   * @method get
   * @param {String} id data id
   * @returns {Object} data
   */

  get : function(id) {
    return tc.u.get(this._data, id, {})
  },

  /**
   * retrieve data from server. at this point this expects a json
   * string response, with the actual data keyed at the id you provided
   *
   * ##example(s)
   *
   * examples/data.load.js
   *
   * @method load
   * @param {String} id data identification key
   * @param {Object} [config] object literal holding options
   * @param {Function} [config.callback] called when data is available
   * @param {Boolean} [config.reload] if true data will be re-fetched even if already loaded
   */

  load : function(id, config) {

    var callback = tc.u.get(config, "callback");

    // check if data is already loaded
    if(this._data[id] && !tc.u.get(config, "reload")) {
      var payload = {id:id, data:this._data[id]}
      $(this).trigger("load-"+id, payload);
      if(callback)
        callback(payload)
      return;
    }

    // attach callback to load event
    if(callback) {
      $(this).on("load-"+id, function(ev, payload) { callback(payload) });
    }

    // check if data is currently being loaded
    if(this._loading[id]) {
      console.log("already loading",id,"skipping additional load")
      return;
    }

    // data is not loaded AND not currently loading, attempt to load data

    // in order to load data we need to find a suitable loader for it

    var loader = this.loaders.loader(id, config);
    this._loading[id] = new Date().getTime()
    loader.load(
      {
        success : function(data) {
          twentyc.data._data[id] = data
          twentyc.data._loading[id] = false;
          $(twentyc.data).trigger("load", { id:id, data:data} );
          $(twentyc.data).trigger("load-"+id, { id:id, data:data });
          $(twentyc.data).off("load-"+id);
        }
      }
    );
  },

}

/**
 * create and manage data loaders
 * @module twentyc
 * @namesapce twentyc.data
 * @class loaders
 * @extends twentyc.cls.Registry
 * @static
 */

twentyc.data.LoaderRegistry = twentyc.cls.extend(
  "LoaderRegistry",
  {
    LoaderRegistry : function() {
      this.Registry();

      /**
       * holds data id -> loader assignments, keyed by data id
       * @property _loaders
       * @type Object
       * @private
       */

      this._loaders = {};
    },

    /**
     * assign loader to data id
     * @method assign
     * @param {String} id data id
     * @param {String} loaderName name that you registered the loader under
     */

    assign : function(id, loaderName) {

      // this will error if loaderName is not registered
      var loader = this.get(loaderName);

      // link loader
      this._loaders[id] = loaderName;

    },

    /**
     * get loader linked to data id via __link__
     * @method loader
     * @param {String} id data_id
     * @returns {Object} loader instance of loader
     */

    loader : function(id, config) {
      if(!this._loaders[id])
        throw("Could not find suitable loader for data id "+id+", are you certain it's assigned?");

      var loader = this.get(this._loaders[id]);
      return new loader(id, config || {});
    }

  },
  twentyc.cls.Registry
);
twentyc.data.loaders = new twentyc.data.LoaderRegistry();

/**
 * any new loader you register should at the very least
 * extend this loader
 *
 * @class Base
 * @module twentyc
 * @namespace twentyc.data.loaders.get
 * @constructor
 * @param {String} id data id
 * @param {Object} [config] object literal holding config attributes
 */
twentyc.data.loaders.register(
  "Base",
  {
    Base : function(id, config) {
      this.dataId = id;
      this.config = config || {};
    },
    retrieve : function(data) {
      var set = tc.u.get(data, this.dataId)
      if(typeof set == undefined)
        return {};
      return set;
    },
    load : function() {
      throw("The load() function needs to be overwritten to do something")
    }
  }
);

/**
 * you may use this loader as a base class for any xhr data retrieval
 * loaders you define. during the ctor you will need to set the url
 * attribute on this.config
 *
 * @class Base
 * @module twentyc
 * @namespace twentyc.data.loaders.get
 * @constructor
 * @param {String} id data id
 * @param {Object} [config] object literal holding config attributes
 * @param {String} [config.url] url of the request
 * @param {Object} [config.data] parameters to send
 */

twentyc.data.loaders.register(
  "XHRGet",
  {
    load : function(callbacks) {
      var url = tc.u.get(this.config, "url");
      var loader = this;
      if(url == undefined)
        throw("XHRGet loader needs url, "+this._meta.name);
      $.ajax(
        {
          url : url,
          data : this.config.data,
          success : function(data) {
            if(typeof callbacks.success == "function")
              callbacks.success(loader.retrieve(data));
          }
        }
      ).fail(function(response) {
        if(callbacks.error)
          callbacks.error(response);
      });
    }
  },
  "Base"
);

/**
 * shortcuts
 */

tc = {
  u : twentyc.util,
  def : twentyc.cls.define,
  ext : twentyc.cls.extend
}

})();
<commit_msg>fix ident<commit_after>(function() {

/**
 * create root namespace for all twentyc definitions
 * @module twentyc
 */

twentyc = {};

/**
 * class helper functions
 * @module twentyc
 * @class cls
 * @static
 */

twentyc.cls = {

  /**
   * converts a string into a standardized class name, replacing
   * invalid characters with valid ones.
   *
   *     twentyc.cls.make_name("class-a"); // classA
   *     twentyc.cls.make_name("class a b"); // classAB
   *     twentyc.cls.make_name("Class-A_B"); // ClassA_B
   *
   * @method make_name
   * @param {String} name base name
   * @returns {String} class_name changed name
   */

  make_name : function(name) {
    var i, names = name.split(/[-\s]/);
    for(i = 0; i < names.length; i++) {
      if(i > 0)
        names[i] = names[i].charAt(0).toUpperCase() + names[i].slice(1);
    }
    return names.join("");
  },

  /**
   * create a new class - if you wish to extend a class take a look at
   * {{#crossLink "cls/extend:method"}}twentyc.cls.extend{{/crossLink}}
   * instead
   *
   * you may define a constructor in the definition by using the class
   * name you provide at __name__
   *
   * Note that if the name you provide is not a valid variable name
   * it will be passed through twentyc.cls.make_name to make it valid
   *
   * ##examples
   *
   * * define and instantiate a class: examples/cls.define.js
   *
   * @method define
   * @param {String} name name or unique identifier of the new class
   * @param {Object} definition object literal of properties and functions that you wish define or redefine
   * @returns {Function} dest constructor of new class
   */

  define : function(name, definition) {

    var k;

    name = twentyc.cls.make_name(name);

    if(typeof(definition[name]) == "function") {
      // a constructor has been provided
      var ctor = definition[name]
      delete definition[name]
    } else {
      // no constructor provided, substitute empty constructor
      var ctor = function(){};
    }

    // cycle through definition and copy to class prototype
    for(k in definition) {
      ctor.prototype[k] = definition[k]
    }

    // create meta information
    ctor.prototype._meta = {
      "name" : name
    }

    return ctor

  },

  /**
   * extend an existing class with new properties and functions
   *
   * if a function is defined that already exists on the parent class
   * this function will be overwritten and a reference to the original
   * function will be provided at parentClassName_functionName
   *
   * you may define a constructor in the definition by using the class
   * name you provide at __name__
   *
   * Note that if the name you provide is not a valid variable name
   * it will be passed through twentyc.cls.make_name to make it valid
   *
   * ##examples
   *
   * * extend and instantiate a class: examples/cls.extend.js
   * * handling method override: examples/cls.extend.method-override.js
   *
   * @method extend
   * @param {String} name name or unqiue identifier of the new class
   * @param {Object} definition object literal of properties and functions that you wish define or redefine
   * @param {Function} [parent] constructor of class
   * that you wish to extend, if omitted an empty function will be substituted
   * @returns {Function} dest constructor of new class
   */

  extend : function(name, definition, parent) {

    var k;
    name = twentyc.cls.make_name(name);

    if(typeof(definition[name]) == "function") {
      // a constructor has been provided
      var ctor = definition[name]
      delete definition[name]
    } else {
      // no constructor provided, substitute empty constructor
      var ctor = function(){
        parent.apply(this, arguments)
      };
    }

    // cycle through parent prototype and copy to class prototype
    for(k in parent.prototype) {
      ctor.prototype[k] = parent.prototype[k]
    }

    // cycle through definition and copy to class prototype
    for(k in definition) {
      if(typeof(ctor.prototype[k]) == "function") {
        // function was already defined by parent, store backref
        ctor.prototype[parent.prototype._meta.name+"_"+k] = parent.prototype[k];
      }
      ctor.prototype[k] = definition[k]
    }

    // reference parent constructor
    ctor.prototype[parent.prototype._meta.name] = parent

    // create meta information
    ctor.prototype._meta = {
      "name" : name,
      "parent" : parent
    }

    return ctor

  },

  /**
   * overrides a method on the provided class
   *
   * @method override
   * @param {Function} destClass A class created via __twentyc.cls.define__ or __twentyc.cls.extend__
   * @param {String} methodName name of method that you wish to override
   * @param {Function} method new method
   */

  override : function(destClass, methodName, method) {

    // create reference to old method
    if(destClass.prototype[methodName])
      destClass.prototype[destClass.prototype._meta.name+"_"+methodName] = destClass.prototype[methodName];

    // override
    destClass.prototype[methodName] = method;

  }

}

/**
 * class registry object - allows you to quickly define and extend
 * similar classes
 *
 * @module twentyc
 * @namespace twentyc.cls
 * @class Registry
 * @constructor
 */

twentyc.cls.Registry = twentyc.cls.define(
  "Registry",
  {

    Registry : function() {

      /**
       * holds the classes defined in this registry
       * @property _classes
       * @type Object
       * @private
       */

      this._classes = {}

    },

    /**
     * register a new class - look at twentyc.cls.define and twentyc.cls.extend for param
     * explanation
     * @method register
     * @param {String} name class name
     * @param {Object} definition class definition
     * @param {String} extend if passed, extend this class from this class (needs to exist in the Registry)
     * @returns {Function} class ctor of the newly created class
     */

    register : function(name, definition, extend) {
      if(this._classes[name] != undefined) {
        throw("Class with name '"+name+"' already exists - name must be unique in the Registry");
      }
      if(extend && typeof this._classes[extend] != "function") {
        throw("Trying to extend from class unknown to this Registry: "+extend);
      }
      if(extend)
        this._classes[name] = twentyc.cls.extend(name, definition, this._classes[extend]);
      else
        this._classes[name] = twentyc.cls.define(name, definition);
      return this._classes[name];
    },

    /**
     * get a registered class constructor
     * @method get
     * @param {String} name class name
     * @returns {Function} class ctor of registered class
     */

    get : function(name) {
      if(typeof this._classes[name] != "function")
        throw("Trying to retrieve class unknown to this Registry: "+name);
      return this._classes[name];
    }
  }
);

/**
 * utility functions
 * @module twentyc
 * @class util
 * @static
 */

twentyc.util = {

  /**
   * retrieve value from object literal - allows you to pass null or undefined
   * as the object and will return null if you do
   *
   * @method get
   * @param {Object} obj
   * @param {String) key
   * @param {Mixed} [default] return this if obj is null or key does not exist
   * @returns {Mixed} value
   */

  get : function(obj, key, dflt) {
    if(!obj || obj[key] == undefined)
      return dflt;
    return obj[key]
  }

}

/**
 * data retrieval, storage and management
 *
 * assures that data is only retrieved once even when multiple sources
 * are requesting it.
 *
 * data is cached locally for quick retrieval afterwards
 *
 * @module twentyc
 * @class data
 * @static
 */

twentyc.data = {

  /**
   * fires everytime a dataset is retrieved from the server - __*__ is
   * substituted with the data id
   *
   * all handlers bound to this event will be removed after its been
   * triggered - in order to permanently subscribe a load event
   * subscribe to "load"
   *
   * @event load-*
   * @param {String} id data id
   * @param {Object} data object literal of retrieved data
   */

  /**
   * fires everytime a dataset is retrieved from the server
   *
   * @event load
   * @param {String} id data id
   * @param {Object} data object literal of retrieved data
   */

  /**
   * keeps track of current loading status
   * @property _loading
   * @type Object
   * @private
   */

  _loading : {},

  /**
   * _data storage, keyed by data id
   * @property data
   * @type Object
   * @private
   */

  _data : {},

  /**
   * attempts to retrieve and return a data set
   *
   * ##example(s)
   *
   * examples/data.load.js
   *
   * @method get
   * @param {String} id data id
   * @returns {Object} data
   */

  get : function(id) {
    return tc.u.get(this._data, id, {})
  },

  /**
   * retrieve data from server. at this point this expects a json
   * string response, with the actual data keyed at the id you provided
   *
   * ##example(s)
   *
   * examples/data.load.js
   *
   * @method load
   * @param {String} id data identification key
   * @param {Object} [config] object literal holding options
   * @param {Function} [config.callback] called when data is available
   * @param {Boolean} [config.reload] if true data will be re-fetched even if already loaded
   */

  load : function(id, config) {

    var callback = tc.u.get(config, "callback");

    // check if data is already loaded
    if(this._data[id] && !tc.u.get(config, "reload")) {
      var payload = {id:id, data:this._data[id]}
      $(this).trigger("load-"+id, payload);
      if(callback)
        callback(payload)
      return;
    }

    // attach callback to load event
    if(callback) {
      $(this).on("load-"+id, function(ev, payload) { callback(payload) });
    }

    // check if data is currently being loaded
    if(this._loading[id]) {
      console.log("already loading",id,"skipping additional load")
      return;
    }

    // data is not loaded AND not currently loading, attempt to load data

    // in order to load data we need to find a suitable loader for it

    var loader = this.loaders.loader(id, config);
    this._loading[id] = new Date().getTime()
    loader.load(
      {
        success : function(data) {
          twentyc.data._data[id] = data
          twentyc.data._loading[id] = false;
          $(twentyc.data).trigger("load", { id:id, data:data} );
          $(twentyc.data).trigger("load-"+id, { id:id, data:data });
          $(twentyc.data).off("load-"+id);
        }
      }
    );
  },

}

/**
 * create and manage data loaders
 * @module twentyc
 * @namesapce twentyc.data
 * @class loaders
 * @extends twentyc.cls.Registry
 * @static
 */

twentyc.data.LoaderRegistry = twentyc.cls.extend(
  "LoaderRegistry",
  {
    LoaderRegistry : function() {
      this.Registry();

      /**
       * holds data id -> loader assignments, keyed by data id
       * @property _loaders
       * @type Object
       * @private
       */

      this._loaders = {};
    },

    /**
     * assign loader to data id
     * @method assign
     * @param {String} id data id
     * @param {String} loaderName name that you registered the loader under
     */

    assign : function(id, loaderName) {

      // this will error if loaderName is not registered
      var loader = this.get(loaderName);

      // link loader
      this._loaders[id] = loaderName;

    },

    /**
     * get loader linked to data id via __link__
     * @method loader
     * @param {String} id data_id
     * @returns {Object} loader instance of loader
     */

    loader : function(id, config) {
      if(!this._loaders[id])
        throw("Could not find suitable loader for data id "+id+", are you certain it's assigned?");

      var loader = this.get(this._loaders[id]);
      return new loader(id, config || {});
    }

  },
  twentyc.cls.Registry
);
twentyc.data.loaders = new twentyc.data.LoaderRegistry();

/**
 * any new loader you register should at the very least
 * extend this loader
 *
 * @class Base
 * @module twentyc
 * @namespace twentyc.data.loaders.get
 * @constructor
 * @param {String} id data id
 * @param {Object} [config] object literal holding config attributes
 */
twentyc.data.loaders.register(
  "Base",
  {
    Base : function(id, config) {
      this.dataId = id;
      this.config = config || {};
    },
    retrieve : function(data) {
      var set = tc.u.get(data, this.dataId)
      if(typeof set == undefined)
        return {};
      return set;
    },
    load : function() {
      throw("The load() function needs to be overwritten to do something")
    }
  }
);

/**
 * you may use this loader as a base class for any xhr data retrieval
 * loaders you define. during the ctor you will need to set the url
 * attribute on this.config
 *
 * @class Base
 * @module twentyc
 * @namespace twentyc.data.loaders.get
 * @constructor
 * @param {String} id data id
 * @param {Object} [config] object literal holding config attributes
 * @param {String} [config.url] url of the request
 * @param {Object} [config.data] parameters to send
 */

twentyc.data.loaders.register(
  "XHRGet",
  {
    load : function(callbacks) {
      var url = tc.u.get(this.config, "url");
      var loader = this;
      if(url == undefined)
        throw("XHRGet loader needs url, "+this._meta.name);
      $.ajax(
        {
          url : url,
          data : this.config.data,
          success : function(data) {
            if(typeof callbacks.success == "function")
              callbacks.success(loader.retrieve(data));
          }
        }
      ).fail(function(response) {
        if(callbacks.error)
          callbacks.error(response);
      });
    }
  },
  "Base"
);

/**
 * shortcuts
 */

tc = {
  u : twentyc.util,
  def : twentyc.cls.define,
  ext : twentyc.cls.extend
}

})();
<|endoftext|>


<commit_before>/*
 * Paper.js - The Swiss Army Knife of Vector Graphics Scripting.
 * http://paperjs.org/
 *
 * Copyright (c) 2011 - 2013, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * All rights reserved.
 */

 /**
  * @name Component
  * @class
  */
var Component = Base.extend(Callback, /** @lends Component# */{
	_class: 'Component',
	_events: [ 'onChange', 'onClick' ],

	_types: {
		'boolean': {
			type: 'checkbox',
			value: 'checked'
		},

		string: {
			type: 'text'
		},

		number: {
			type: 'number',
			number: true
		},

		button: {
			type: 'button'
		},

		text: {
			tag: 'div',
			value: 'text'
		},

		slider: {
			type: 'range',
			number: true
		},

		list: {
			tag: 'select',

			options: function() {
				DomElement.removeChildren(this._inputItem);
				DomElement.create(Base.each(this._options, function(option) {
					this.push('option', { value: option, text: option });
				}, []), this._inputItem);
			}
		}
	},

	initialize: function Component(obj) {
		this._type = obj.type in this._types
			? obj.type
			: 'options' in obj
				? 'list'
				: 'onClick' in obj
					? 'button'
					: typeof obj.value;
		this._info = this._types[this._type] || { type: this._type };
		var that = this,
			dontFire = true;
		this._inputItem = DomElement.create(this._info.tag || 'input', {
			type: this._info.type,
			events: {
				change: function() {
					that.setValue(
						DomElement.get(this, that._info.value || 'value'),
						dontFire);
				},
				click: function() {
					that.fire('click');
				}
			}
		});
		// Attach default 'change' even that delegates to palette
		this.attach('change', function(value) {
			if (!dontFire)
				this._palette.fire('change', this, this.name, value);
		});
		this._element = DomElement.create('tr', [
			this._labelItem = DomElement.create('td'),
			'td', [this._inputItem]
		]);
		Base.each(obj, function(value, key) {
			this[key] = value;
		}, this);
		this._defaultValue = this._value;
		// Start firing change events after we have initalized.
		dontFire = false;
	},

	getType: function() {
		return this._type;
	},

	getLabel: function() {
		return this._label;
	},

	setLabel: function(label) {
		this._label = label;
		DomElement.set(this._labelItem, 'text', label + ':');
	},

	getOptions: function() {
		return this._options;
	},

	setOptions: function(options) {
		this._options = options;
		if (this._info.options)
			this._info.options.call(this);
	},

	getValue: function() {
		return this._value;
	},

	setValue: function(value, _dontFire) {
		var key = this._info.value || 'value';
		DomElement.set(this._inputItem, key, value);
		// Read back and convert from input again, to make sure we're in sync
		value = DomElement.get(this._inputItem, key);
		if (this._info.number)
			value = parseFloat(value, 10);
		if (this._value !== value) {
			this._value = value;
			if (!_dontFire)
				this.fire('change', value);
		}
	},

	getRange: function() {
		return [parseFloat(DomElement.get(this._inputItem, 'min')),
				parseFloat(DomElement.get(this._inputItem, 'max'))];
	},

	setRange: function(min, max) {
		var range = Array.isArray(min) ? min : [min, max];
		DomElement.set(this._inputItem, { min: range[0], max: range[1] });
	},

	getMin: function() {
		return this.getRange()[0];
	},

	setMin: function(min) {
		this.setRange(min, this.getMax());
	},

	getMax: function() {
		return this.getRange()[1];
	},

	setMax: function(max) {
		this.setRange(this.getMin(), max);
	},

	getStep: function() {
		return parseFloat(DomElement.get(this._inputItem, 'step'));
	},

	setStep: function(step) {
		DomElement.set(this._inputItem, 'step', step);
	},

	reset: function() {
		this.setValue(this._defaultValue);
	}
});<commit_msg>Add support for color components.<commit_after>/*
 * Paper.js - The Swiss Army Knife of Vector Graphics Scripting.
 * http://paperjs.org/
 *
 * Copyright (c) 2011 - 2013, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * All rights reserved.
 */

 /**
  * @name Component
  * @class
  */
var Component = Base.extend(Callback, /** @lends Component# */{
	_class: 'Component',
	_events: [ 'onChange', 'onClick' ],

	// Meta-information, by type. This is stored in _meta on the components.
	_types: {
		'boolean': {
			type: 'checkbox',
			value: 'checked'
		},

		string: {
			type: 'text'
		},

		number: {
			type: 'number',
			number: true
		},

		button: {
			type: 'button'
		},

		text: {
			tag: 'div',
			// This will return the native textContent through DomElement.get():
			value: 'text'
		},

		slider: {
			type: 'range',
			number: true
		},

		list: {
			tag: 'select',

			setOptions: function() {
				DomElement.removeChildren(this._input);
				DomElement.create(Base.each(this._options, function(option) {
					this.push('option', { value: option, text: option });
				}, []), this._input);
			}
		},

		color: {
			type: 'color',

			getValue: function(value) {
				return new Color(value);
			},

			setValue: function(value) {
				// Only enfore hex values if the input field is indeed of
				// color type. This allows sketch.paperjs.org to plug in
				// the Spectrum.js library with alpha support.
				return new Color(value).toCSS(
						DomElement.get(this._input, 'type') === 'color');
			}
		}
	},

	initialize: function Component(obj) {
		this._id = Component._id = (Component._id || 0) + 1;
		this._type = obj.type in this._types
			? obj.type
			: 'options' in obj
				? 'list'
				: 'onClick' in obj
					? 'button'
					: typeof obj.value;
		this._meta = this._types[this._type] || { type: this._type };
		var that = this,
			dontFire = true,
			id = 'component-' + this._id;
		this._input = DomElement.create(this._meta.tag || 'input', {
			id: id,
			type: this._meta.type,
			events: {
				change: function() {
					that.setValue(
						DomElement.get(this, that._meta.value || 'value'),
						dontFire);
				},
				click: function() {
					that.fire('click');
				}
			}
		});
		// Attach default 'change' even that delegates to palette
		this.attach('change', function(value) {
			if (!dontFire)
				this._palette.fire('change', this, this.name, value);
		});
		this._element = DomElement.create('tr', [
			'td', [this._label = DomElement.create('label', { 'for': id })],
			'td', [this._input]
		]);
		Base.each(obj, function(value, key) {
			this[key] = value;
		}, this);
		this._defaultValue = this._value;
		// Start firing change events after we have initalized.
		dontFire = false;
	},

	getType: function() {
		return this._type;
	},

	getLabel: function() {
		// Prefix wit '__' since _label is already the element.
		return this.__label;
	},

	setLabel: function(label) {
		this.__label = label;
		DomElement.set(this._label, 'text', label + ':');
	},

	getOptions: function() {
		return this._options;
	},

	setOptions: function(options) {
		this._options = options;
		var setOptions = this._meta.setOptions;
		if (setOptions)
			setOptions.call(this);
	},

	getValue: function() {
		var value = this._value,
			getValue = this._meta.getValue;
		return getValue ? getValue.call(this, value) : value;
	},

	setValue: function(value, _dontFire) {
		var key = this._meta.value || 'value',
			setValue = this._meta.setValue;
		if (setValue)
			value = setValue.call(this, value);
		DomElement.set(this._input, key, value);
		// Read back and convert from input again, to make sure we're in sync
		value = DomElement.get(this._input, key);
		if (this._meta.number)
			value = parseFloat(value, 10);
		if (this._value !== value) {
			this._value = value;
			if (!_dontFire)
				this.fire('change', value);
		}
	},

	getRange: function() {
		return [parseFloat(DomElement.get(this._input, 'min')),
				parseFloat(DomElement.get(this._input, 'max'))];
	},

	setRange: function(min, max) {
		var range = Array.isArray(min) ? min : [min, max];
		DomElement.set(this._input, { min: range[0], max: range[1] });
	},

	getMin: function() {
		return this.getRange()[0];
	},

	setMin: function(min) {
		this.setRange(min, this.getMax());
	},

	getMax: function() {
		return this.getRange()[1];
	},

	setMax: function(max) {
		this.setRange(this.getMin(), max);
	},

	getStep: function() {
		return parseFloat(DomElement.get(this._input, 'step'));
	},

	setStep: function(step) {
		DomElement.set(this._input, 'step', step);
	},

	reset: function() {
		this.setValue(this._defaultValue);
	}
});<|endoftext|>


<commit_before>"use strict"
/* eslint-disable no-console */

// defines m
import "./vendor/mithril.js"

import NameTracker from "./NameTracker.js"
import { FileUtils } from "./FileUtils.js"

import { Pointrel20190820 } from "./Pointrel20190820.js"
import { CanonicalJSON } from "./CanonicalJSON.js"

const p = new Pointrel20190820()

p.setDefaultApplicationName("organizer")

const nameTracker = new NameTracker({
    hashNameField: "name",
    displayNameLabel: "Name",
    defaultName: "organizer-test",
    nameChangedCallback: startup
})

function getOrganizerName() {
    if (!nameTracker.name || nameTracker.name === "organizer") return "organizer"
    return "organizer:" + nameTracker.name
}

const sortOptions = ["order", "date", "subject", "from"]
let sortBy = "order"

const bodyLoaded = {}

class Item {
    constructor(uuid) {
        this.uuid = uuid || p.uuidv4()
    }

    getType() {
        return p.findC(this.uuid, "type")
    }

    setType(type) {
        p.addTriple(this.uuid, "type", type)
    }

    getTitle() {
        return p.findC(this.uuid, "title") || ""
    }

    setTitle(title) {
        p.addTriple(this.uuid, "title", title)
    }

    getFrom() {
        return p.findC(this.uuid, "from") || ""
    }

    setFrom(title) {
        p.addTriple(this.uuid, "from", title)
    }

    getDate() {
        let result = p.findC(this.uuid, "date") || ""
        if (result) return new Date(Date.parse(result)).toISOString().replace("T", " ")
    }

    setDate(title) {
        p.addTriple(this.uuid, "date", title)
    }

    getBody() {
        const body = p.findC(this.uuid, "body") || ""
        if (!body && !bodyLoaded[CanonicalJSON.stringify(this.uuid)]) {
            bodyLoaded[CanonicalJSON.stringify(this.uuid)] = true
            p.loadFile(this.uuid)
        }
        return body
    }

    setBody(body) {
        p.addTriple(this.uuid, "body", body)
    }
}

class Organizer {
    getItems() {
        const result = []
        const bcMap = p.findBC(getOrganizerName(), "item")
        for (let key in bcMap) {
            const uuid = bcMap[key]
            if (uuid) result.push(new Item(uuid))
        }
        return result
    }

    addItem(item) {
        p.addTriple(getOrganizerName(), {item: item.uuid}, item.uuid)
    }

    deleteItem(item) {
        p.addTriple(getOrganizerName(), {item: item.uuid}, null)
    }
}

let organizer = new Organizer()

function makeNewItem() {
    const item = new Item()
    organizer.addItem(item)
}

function importMailbox() {
    FileUtils.loadFromFile((name, contents) => {
        console.log("importMailbox", name)
        const emails = contents.split(/^From /m)
        let emailCount = 0
        for (let email of emails) {
            if (!email || email === " ") continue
            // email = email.replace(/^>From /m, "From ")
            email = "From " + email
            // console.log("email", email)
            const subject = email.match(/^Subject: ([^\n]*)/m)
            const messageId = email.match(/^Message-ID: <([^>]*)/m)[1]
            if (!messageId) throw new Error("missing message ID")

            const title = subject ? subject[1] : ""

            const fromMatch = email.match(/^From: ([^\n]*)/m)
            const from = fromMatch ? fromMatch[1] : ""
            const dateMatch = email.match(/^Date: ([^\n]*)/m)
            const date = dateMatch ? dateMatch[1] : ""

            const uuid = {type: "email", messageId}

            emailCount++
            console.log("subject", title)
            console.log("messageId", messageId)
            console.log("==============================", emailCount)

            const item = new Item(uuid)

            // Save indexing info to organizer stream
            p.newTransaction("organizer/importMailbox")
            item.setTitle(title)
            item.setFrom(from)
            item.setDate(date)
            // item.setMessageId(messageId)
            // item.setBody(email)
            item.setType("email:mbox")
            organizer.addItem(item)
            p.sendCurrentTransaction()

            // Save body of email to individual item stream
            p.newTransaction("organizer/importMailbox")
            // item.setTitle(title)
            // item.setFrom(from)
            // item.setDate(date)
            item.setBody(email)
            // item.setType("email:mbox")
            organizer.addItem(item)
            p.sendCurrentTransaction(uuid)

            // TODO: Now need to keep reference to the email and index information
            m.redraw()
        }
    })
}

let currentUUID = null
let editMode = false

function displayItemEditor(item) {
    return m("div.ba.bw1.pa1", [
        m("span", "UUID: ", " ", JSON.stringify(item.uuid)),
        m("br"),
        m("span", "Title:"),
        m("input.ml1.w-90", { value: item.getTitle(), onchange: (event) => item.setTitle(event.target.value) }),
        m("br"),
        "Text:",
        m("br"),
        m("textarea", { rows: 5, cols: 60,  value: item.getBody(), onchange: (event) => item.setBody(event.target.value) }),
    ])
}

const showHeaders = {}

function rtrim(string) {
    // Trim trailing space from string
    return string.replace(/\s*$/,"")
}

function displayItemContents(item) {
    const body = item.getBody()
    let headers = ""
    let rest = body
    if (body.startsWith("From ")) {
        headers = body.split(/\n\s*\n/)[0]
        if (headers.length === body.length) {
            headers = ""
        } else {
            headers = rtrim(headers)
        }
        rest = body.substring(headers.length)
    }
    rest = rest.trim() || ""
    return m("div.ba.bw1.pa1", [
        headers ? [
            m("button.f6.mr1", { onclick: () => showHeaders[item.uuid] = !showHeaders[item.uuid] }, "Headers"),
        ] : [],
        m("span", "UUID: ", " ", JSON.stringify(item.uuid)),
        showHeaders[item.uuid] ? m("pre", {style: "white-space: pre-wrap"}, headers) : [],
        m("pre", {style: "white-space: pre-wrap"}, rest),
    ])

}

// TODO: optimize exessive stringify use
function isUUIDMatch(a, b) {
    return JSON.stringify(a) === JSON.stringify(b)
}

function displayItem(item, index) {
    const from = item.getFrom()
    const fromName = (from.split(" <")[0] || "").replace(/"/g, "") || ""
    const fromEmail = "<" + (from.split(" <")[1] || ">") || ""
    const date = item.getDate()
    return m("div.mt1", {key: JSON.stringify(item.uuid)}, [
        m("button.f6", {onclick: () => {
            currentUUID = (isUUIDMatch(currentUUID, item.uuid) && !editMode) ? null : item.uuid
            editMode = false
        }}, "Show"),
        m("button.ml1.mr1.f6", {onclick: () => {
            currentUUID = (isUUIDMatch(currentUUID, item.uuid) && editMode) ? null : item.uuid
            editMode = true
        }}, "Edit"),
        m("span.dib.w2.tr", index), " ",
        m("span.ml2", { title: date }, item.getTitle()),
        m("span.ml2.f6", { title: fromEmail }, fromName),
        m("br"),

        (isUUIDMatch(item.uuid, currentUUID) && editMode) ? displayItemEditor(item) : [],
        (isUUIDMatch(item.uuid, currentUUID) && !editMode) ? displayItemContents(item) : []
    ])
}

function sortItems(items) {
    if (sortBy === "order") return
    if (sortBy === "date") return items.sort((a, b) => a.getDate().localeCompare(b.getDate()))
    if (sortBy === "subject") return items.sort((a, b) => {
        let result = a.getTitle().localeCompare(b.getTitle())
        if (result === 0) result = a.getDate().localeCompare(b.getDate())
        return result
    })
    if (sortBy === "from") return items.sort((a, b) => {
        let result = a.getFrom().localeCompare(b.getFrom())
        if (result === 0) result = a.getDate().localeCompare(b.getDate())
        return result
    })
    throw new Error("unexpected sort")
}

function displayItems() {
    const items = organizer.getItems()
    let index = 0
    sortItems(items)
    return m("div.ma1", [
        m("div",
            "Sort by:",
            m("select.ma1", {onchange: event => sortBy = event.target.value},
                sortOptions.map(key => {
                    return m("option", {value: key, selected: sortBy === key}, key)
                })
            ),
        ),
        items.map((item) => {
            return displayItem(item, index++)
        }),
        m("div.mt1", [
            m("button", { onclick: () => makeNewItem() }, "New Item"),
            m("button.ml1", { onclick: () => importMailbox() }, "Import Mailbox")
        ])
    ])
}

const OrganizerViewer = {
    view: function() {
        // console.log("view start")
        const result = m(".main.ma1", [
            p.isOffline() ? m("div.h2.pa1.ba.b--red", "OFFLINE", m("button.ml1", { onclick: p.goOnline }, "Try to go online")) : [],
            nameTracker.displayNameEditor(),
            p.isLoaded() ?
                displayItems() :
                "Loading... " + (p.getLatestSequence() || "")
        ])
        // console.log("view done")
        return result
    }
}

async function startup() {
    p.setRedrawFunction(m.redraw)
    p.setStreamId(getOrganizerName())
    await p.updateFromStorage(true)
    m.redraw()
}

m.mount(document.body, OrganizerViewer)

startup()
<commit_msg>organizer: improve from parsing (for one mailing list)<commit_after>"use strict"
/* eslint-disable no-console */

// defines m
import "./vendor/mithril.js"

import NameTracker from "./NameTracker.js"
import { FileUtils } from "./FileUtils.js"

import { Pointrel20190820 } from "./Pointrel20190820.js"
import { CanonicalJSON } from "./CanonicalJSON.js"

const p = new Pointrel20190820()

p.setDefaultApplicationName("organizer")

const nameTracker = new NameTracker({
    hashNameField: "name",
    displayNameLabel: "Name",
    defaultName: "organizer-test",
    nameChangedCallback: startup
})

function getOrganizerName() {
    if (!nameTracker.name || nameTracker.name === "organizer") return "organizer"
    return "organizer:" + nameTracker.name
}

const sortOptions = ["order", "date", "subject", "from"]
let sortBy = "order"

const bodyLoaded = {}

class Item {
    constructor(uuid) {
        this.uuid = uuid || p.uuidv4()
    }

    getType() {
        return p.findC(this.uuid, "type")
    }

    setType(type) {
        p.addTriple(this.uuid, "type", type)
    }

    getTitle() {
        return p.findC(this.uuid, "title") || ""
    }

    setTitle(title) {
        p.addTriple(this.uuid, "title", title)
    }

    getFrom() {
        return p.findC(this.uuid, "from") || ""
    }

    setFrom(title) {
        p.addTriple(this.uuid, "from", title)
    }

    getDate() {
        let result = p.findC(this.uuid, "date") || ""
        if (result) return new Date(Date.parse(result)).toISOString().replace("T", " ")
    }

    setDate(title) {
        p.addTriple(this.uuid, "date", title)
    }

    getBody() {
        const body = p.findC(this.uuid, "body") || ""
        if (!body && !bodyLoaded[CanonicalJSON.stringify(this.uuid)]) {
            bodyLoaded[CanonicalJSON.stringify(this.uuid)] = true
            p.loadFile(this.uuid)
        }
        return body
    }

    setBody(body) {
        p.addTriple(this.uuid, "body", body)
    }
}

class Organizer {
    getItems() {
        const result = []
        const bcMap = p.findBC(getOrganizerName(), "item")
        for (let key in bcMap) {
            const uuid = bcMap[key]
            if (uuid) result.push(new Item(uuid))
        }
        return result
    }

    addItem(item) {
        p.addTriple(getOrganizerName(), {item: item.uuid}, item.uuid)
    }

    deleteItem(item) {
        p.addTriple(getOrganizerName(), {item: item.uuid}, null)
    }
}

let organizer = new Organizer()

function makeNewItem() {
    const item = new Item()
    organizer.addItem(item)
}

function importMailbox() {
    FileUtils.loadFromFile((name, contents) => {
        console.log("importMailbox", name)
        const emails = contents.split(/^From /m)
        let emailCount = 0
        for (let email of emails) {
            if (!email || email === " ") continue
            // email = email.replace(/^>From /m, "From ")
            email = "From " + email
            // console.log("email", email)
            const subject = email.match(/^Subject: ([^\n]*)/m)
            const messageId = email.match(/^Message-ID: <([^>]*)/m)[1]
            if (!messageId) throw new Error("missing message ID")

            const title = subject ? subject[1] : ""

            const fromMatch = email.match(/^From: ([^\n]*)/m)
            const from = fromMatch ? fromMatch[1] : ""
            const dateMatch = email.match(/^Date: ([^\n]*)/m)
            const date = dateMatch ? dateMatch[1] : ""

            const uuid = {type: "email", messageId}

            emailCount++
            console.log("subject", title)
            console.log("messageId", messageId)
            console.log("==============================", emailCount)

            const item = new Item(uuid)

            // Save indexing info to organizer stream
            p.newTransaction("organizer/importMailbox")
            item.setTitle(title)
            item.setFrom(from)
            item.setDate(date)
            // item.setMessageId(messageId)
            // item.setBody(email)
            item.setType("email:mbox")
            organizer.addItem(item)
            p.sendCurrentTransaction()

            // Save body of email to individual item stream
            p.newTransaction("organizer/importMailbox")
            // item.setTitle(title)
            // item.setFrom(from)
            // item.setDate(date)
            item.setBody(email)
            // item.setType("email:mbox")
            organizer.addItem(item)
            p.sendCurrentTransaction(uuid)

            // TODO: Now need to keep reference to the email and index information
            m.redraw()
        }
    })
}

let currentUUID = null
let editMode = false

function displayItemEditor(item) {
    return m("div.ba.bw1.pa1", [
        m("span", "UUID: ", " ", JSON.stringify(item.uuid)),
        m("br"),
        m("span", "Title:"),
        m("input.ml1.w-90", { value: item.getTitle(), onchange: (event) => item.setTitle(event.target.value) }),
        m("br"),
        "Text:",
        m("br"),
        m("textarea", { rows: 5, cols: 60,  value: item.getBody(), onchange: (event) => item.setBody(event.target.value) }),
    ])
}

const showHeaders = {}

function rtrim(string) {
    // Trim trailing space from string
    return string.replace(/\s*$/,"")
}

function displayItemContents(item) {
    const body = item.getBody()
    let headers = ""
    let rest = body
    if (body.startsWith("From ")) {
        headers = body.split(/\n\s*\n/)[0]
        if (headers.length === body.length) {
            headers = ""
        } else {
            headers = rtrim(headers)
        }
        rest = body.substring(headers.length)
    }
    rest = rest.trim() || ""
    return m("div.ba.bw1.pa1", [
        headers ? [
            m("button.f6.mr1", { onclick: () => showHeaders[item.uuid] = !showHeaders[item.uuid] }, "Headers"),
        ] : [],
        m("span", "UUID: ", " ", JSON.stringify(item.uuid)),
        showHeaders[item.uuid] ? m("pre", {style: "white-space: pre-wrap"}, headers) : [],
        m("pre", {style: "white-space: pre-wrap"}, rest),
    ])

}

// TODO: optimize exessive stringify use
function isUUIDMatch(a, b) {
    return JSON.stringify(a) === JSON.stringify(b)
}

function displayItem(item, index) {
    const from = item.getFrom()
    const fromEmail = "<" + (from.split(" (")[0] || "").replace(/"/g, "") + ">"
    const fromName = "(" + from.split(" (")[1] || ""
    const date = item.getDate()
    return m("div.mt1", {key: JSON.stringify(item.uuid)}, [
        m("button.f6", {onclick: () => {
            currentUUID = (isUUIDMatch(currentUUID, item.uuid) && !editMode) ? null : item.uuid
            editMode = false
        }}, "Show"),
        m("button.ml1.mr1.f6", {onclick: () => {
            currentUUID = (isUUIDMatch(currentUUID, item.uuid) && editMode) ? null : item.uuid
            editMode = true
        }}, "Edit"),
        m("span.dib.w2.tr", index), " ",
        m("span.ml2", { title: date }, item.getTitle()),
        m("span.ml2.f6", { title: fromEmail }, fromName),
        m("br"),

        (isUUIDMatch(item.uuid, currentUUID) && editMode) ? displayItemEditor(item) : [],
        (isUUIDMatch(item.uuid, currentUUID) && !editMode) ? displayItemContents(item) : []
    ])
}

function sortItems(items) {
    if (sortBy === "order") return
    if (sortBy === "date") return items.sort((a, b) => a.getDate().localeCompare(b.getDate()))
    if (sortBy === "subject") return items.sort((a, b) => {
        let result = a.getTitle().localeCompare(b.getTitle())
        if (result === 0) result = a.getDate().localeCompare(b.getDate())
        return result
    })
    if (sortBy === "from") return items.sort((a, b) => {
        let result = a.getFrom().localeCompare(b.getFrom())
        if (result === 0) result = a.getDate().localeCompare(b.getDate())
        return result
    })
    throw new Error("unexpected sort")
}

function displayItems() {
    const items = organizer.getItems()
    let index = 0
    sortItems(items)
    return m("div.ma1", [
        m("div",
            "Sort by:",
            m("select.ma1", {onchange: event => sortBy = event.target.value},
                sortOptions.map(key => {
                    return m("option", {value: key, selected: sortBy === key}, key)
                })
            ),
        ),
        items.map((item) => {
            return displayItem(item, index++)
        }),
        m("div.mt1", [
            m("button", { onclick: () => makeNewItem() }, "New Item"),
            m("button.ml1", { onclick: () => importMailbox() }, "Import Mailbox")
        ])
    ])
}

const OrganizerViewer = {
    view: function() {
        // console.log("view start")
        const result = m(".main.ma1", [
            p.isOffline() ? m("div.h2.pa1.ba.b--red", "OFFLINE", m("button.ml1", { onclick: p.goOnline }, "Try to go online")) : [],
            nameTracker.displayNameEditor(),
            p.isLoaded() ?
                displayItems() :
                "Loading... " + (p.getLatestSequence() || "")
        ])
        // console.log("view done")
        return result
    }
}

async function startup() {
    p.setRedrawFunction(m.redraw)
    p.setStreamId(getOrganizerName())
    await p.updateFromStorage(true)
    m.redraw()
}

m.mount(document.body, OrganizerViewer)

startup()
<|endoftext|>


<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

'use strict';

base.requireStylesheet('ui.quad_view');

base.require('ui');
base.require('ui.quad_view_viewport');

base.exportTo('ui', function() {

  var QuadView = ui.define('x-quad-view');

  QuadView.prototype = {
    __proto__: HTMLUnknownElement.prototype,

    decorate: function() {
      this.quads_ = undefined;
      this.viewport_ = undefined;
      this.deviceViewportSizeForFrame_ = undefined;
      this.canvas_ = document.createElement('canvas');
      this.appendChild(this.canvas_);

      this.onViewportChanged_ = this.onViewportChanged_.bind(this);
      this.onMouseDown_ = this.onMouseDown_.bind(this);
      this.onMouseMove_ = this.onMouseMove_.bind(this);
      this.onMouseUp_ = this.onMouseUp_.bind(this);
      this.canvas_.addEventListener('mousedown', this.onMouseDown_);

      this.canvas_.addEventListener('focus', this.redrawCanvas_.bind(this));
      this.canvas_.addEventListener('blur', this.redrawCanvas_.bind(this));
      this.canvas_.tabIndex = 0;
    },

    get viewport() {
      return this.viewport_;
    },

    set viewport(viewport) {
      if (this.viewport_)
        this.viewport_.removeEventListener('change', this.onViewportChanged_);
      this.viewport_ = viewport;
      if (this.viewport_)
        this.viewport_.addEventListener('change', this.onViewportChanged_);
      this.updateChildren_();
    },

    onViewportChanged_: function() {
      if (!this.hasRequiredProprties_)
        return;
      this.redrawCanvas_();
    },

    get quads() {
      return this.quads_;
    },

    set quads(quads) {
      this.quads_ = quads;
      this.updateChildren_();
    },

    get deviceViewportSizeForFrame() {
      return this.deviceViewportSizeForFrame_;
    },

    set deviceViewportSizeForFrame(size) {
      this.deviceViewportSizeForFrame_ = size;
      this.updateChildren_();
    },

    get hasRequiredProprties_() {
      return this.quads_ &&
          this.viewport_;
    },

    updateChildren_: function() {
      var canvas = this.canvas_;
      if (!this.hasRequiredProprties_) {
        canvas.width = 0;
        canvas.height = 0;
        return;
      }

      this.redrawCanvas_();
    },

    redrawCanvas_: function() {
      if (this.canvas_.width != this.viewport_.deviceWidth) {
        this.canvas_.width = this.viewport_.deviceWidth;
        this.canvas_.style.width = this.viewport_.layoutWidth + 'px';
      }
      if (this.canvas_.height != this.viewport_.deviceHeight) {
        this.canvas_.height = this.viewport_.deviceHeight;
        this.canvas_.style.height = this.viewport_.layoutHeight + 'px';
      }

      var ctx = this.canvas_.getContext('2d');
      var vp = this.viewport_;
      ctx.fillStyle = 'rgb(255,255,255)';
      ctx.fillRect(
          0, 0,
          this.canvas_.width, this.canvas_.height);

      ctx.save();

      vp.applyTransformToContext(ctx);
      ctx.lineWidth = vp.getDeviceLineWidthAssumingTransformIsApplied(1.0);

      var quads = this.quads_;

      // Background colors.
      var lastBackgroundColor = 'rgb(255,255,0)';
      ctx.fillStyle = lastBackgroundColor;
      for (var i = 0; i < quads.length; i++) {
        var quad = quads[i];
        if (!quad.backgroundColor)
          continue;
        if (quad.backgroundColor != lastBackgroundColor) {
          lastBackgroundColor = quad.backgroundColor;
          ctx.fillStyle = lastBackgroundColor;
        }
        ctx.beginPath();
        ctx.moveTo(quad.p1[0], quad.p1[1]);
        ctx.lineTo(quad.p2[0], quad.p2[1]);
        ctx.lineTo(quad.p3[0], quad.p3[1]);
        ctx.lineTo(quad.p4[0], quad.p4[1]);
        ctx.closePath();
        ctx.fill();
      }

      // Outlines.
      for (var i = 0; i < quads.length; i++) {
        var quad = quads[i];
        ctx.beginPath();
        ctx.moveTo(quad.p1[0], quad.p1[1]);
        ctx.lineTo(quad.p2[0], quad.p2[1]);
        ctx.lineTo(quad.p3[0], quad.p3[1]);
        ctx.lineTo(quad.p4[0], quad.p4[1]);
        ctx.closePath();
        if (quad.borderColor)
          ctx.strokeStyle = quad.borderColor;
        else
          ctx.strokeStyle = 'rgb(128,128,128)';
        ctx.stroke();
      }

      // Selection outlines.
      ctx.lineWidth = vp.getDeviceLineWidthAssumingTransformIsApplied(8.0);
      var rules = window.getMatchedCSSRules(this.canvas_);

      // TODO(nduca): Figure out how to get these from css.
      if (document.activeElement == this.canvas_)
        ctx.strokeStyle = 'rgb(187,226,54)';
      else
        ctx.strokeStyle = 'rgb(156,189,45)';

      for (var i = 0; i < quads.length; i++) {
        var quad = quads[i];
        if (!quad.selected)
          continue;
        ctx.beginPath();
        ctx.moveTo(quad.p1[0], quad.p1[1]);
        ctx.lineTo(quad.p2[0], quad.p2[1]);
        ctx.lineTo(quad.p3[0], quad.p3[1]);
        ctx.lineTo(quad.p4[0], quad.p4[1]);
        ctx.closePath();
        ctx.stroke();
      }

      if (this.deviceViewportSizeForFrame_) {
        ctx.lineWidth = vp.getDeviceLineWidthAssumingTransformIsApplied(2.0);
        ctx.strokeStyle = 'rgba(255,0,0,1)';
        ctx.strokeRect(0,
                       0,
                       this.deviceViewportSizeForFrame_.width,
                       this.deviceViewportSizeForFrame_.height);
      }

      ctx.restore();
    },

    selectQuadsAtCanvasClientPoint: function(clientX, clientY) {
      var selectedQuadIndices = this.findQuadsAtCanvasClientPoint(
          clientX, clientY);
      var e = new base.Event('selectionChanged');
      e.selectedQuadIndices = selectedQuadIndices;
      this.dispatchEvent(e);
      this.viewport_.forceRedrawAll();
    },

    findQuadsAtCanvasClientPoint: function(clientX, clientY) {
      var bounds = this.canvas_.getBoundingClientRect();
      var vecInLayout = vec2.createXY(clientX - bounds.left,
                                      clientY - bounds.top);
      var vecInWorldPixels =
          this.viewport_.layoutPixelsToWorldPixels2(vecInLayout);

      var quads = this.quads_;
      var hitIndices = [];
      for (var i = 0; i < quads.length; i++) {
        var hit = quads[i].vecInside(vecInWorldPixels);
        if (hit)
          hitIndices.push(i);
      }
      return hitIndices;
    },

    onMouseDown_: function(e) {
      this.selectQuadsAtCanvasClientPoint(e.clientX, e.clientY);
      document.addEventListener('mousemove', this.onMouseMove_);
      document.addEventListener('mouseup', this.onMouseUp_);
      e.preventDefault();
      this.canvas_.focus();
      return true;
    },

    onMouseMove_: function(e) {
      this.selectQuadsAtCanvasClientPoint(e.clientX, e.clientY);
    },

    onMouseUp_: function(e) {
      this.selectQuadsAtCanvasClientPoint(e.clientX, e.clientY);
      document.removeEventListener('mousemove', this.onMouseMove_);
      document.removeEventListener('mouseup', this.onMouseUp_);
    }

  };

  return {
    QuadView: QuadView
  };
});
<commit_msg>Color viewport blue<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

'use strict';

base.requireStylesheet('ui.quad_view');

base.require('ui');
base.require('ui.quad_view_viewport');

base.exportTo('ui', function() {

  var QuadView = ui.define('x-quad-view');

  QuadView.prototype = {
    __proto__: HTMLUnknownElement.prototype,

    decorate: function() {
      this.quads_ = undefined;
      this.viewport_ = undefined;
      this.deviceViewportSizeForFrame_ = undefined;
      this.canvas_ = document.createElement('canvas');
      this.appendChild(this.canvas_);

      this.onViewportChanged_ = this.onViewportChanged_.bind(this);
      this.onMouseDown_ = this.onMouseDown_.bind(this);
      this.onMouseMove_ = this.onMouseMove_.bind(this);
      this.onMouseUp_ = this.onMouseUp_.bind(this);
      this.canvas_.addEventListener('mousedown', this.onMouseDown_);

      this.canvas_.addEventListener('focus', this.redrawCanvas_.bind(this));
      this.canvas_.addEventListener('blur', this.redrawCanvas_.bind(this));
      this.canvas_.tabIndex = 0;
    },

    get viewport() {
      return this.viewport_;
    },

    set viewport(viewport) {
      if (this.viewport_)
        this.viewport_.removeEventListener('change', this.onViewportChanged_);
      this.viewport_ = viewport;
      if (this.viewport_)
        this.viewport_.addEventListener('change', this.onViewportChanged_);
      this.updateChildren_();
    },

    onViewportChanged_: function() {
      if (!this.hasRequiredProprties_)
        return;
      this.redrawCanvas_();
    },

    get quads() {
      return this.quads_;
    },

    set quads(quads) {
      this.quads_ = quads;
      this.updateChildren_();
    },

    get deviceViewportSizeForFrame() {
      return this.deviceViewportSizeForFrame_;
    },

    set deviceViewportSizeForFrame(size) {
      this.deviceViewportSizeForFrame_ = size;
      this.updateChildren_();
    },

    get hasRequiredProprties_() {
      return this.quads_ &&
          this.viewport_;
    },

    updateChildren_: function() {
      var canvas = this.canvas_;
      if (!this.hasRequiredProprties_) {
        canvas.width = 0;
        canvas.height = 0;
        return;
      }

      this.redrawCanvas_();
    },

    redrawCanvas_: function() {
      if (this.canvas_.width != this.viewport_.deviceWidth) {
        this.canvas_.width = this.viewport_.deviceWidth;
        this.canvas_.style.width = this.viewport_.layoutWidth + 'px';
      }
      if (this.canvas_.height != this.viewport_.deviceHeight) {
        this.canvas_.height = this.viewport_.deviceHeight;
        this.canvas_.style.height = this.viewport_.layoutHeight + 'px';
      }

      var ctx = this.canvas_.getContext('2d');
      var vp = this.viewport_;
      ctx.fillStyle = 'rgb(255,255,255)';
      ctx.fillRect(
          0, 0,
          this.canvas_.width, this.canvas_.height);

      ctx.save();

      vp.applyTransformToContext(ctx);
      ctx.lineWidth = vp.getDeviceLineWidthAssumingTransformIsApplied(1.0);

      var quads = this.quads_;

      // Background colors.
      var lastBackgroundColor = 'rgb(255,255,0)';
      ctx.fillStyle = lastBackgroundColor;
      for (var i = 0; i < quads.length; i++) {
        var quad = quads[i];
        if (!quad.backgroundColor)
          continue;
        if (quad.backgroundColor != lastBackgroundColor) {
          lastBackgroundColor = quad.backgroundColor;
          ctx.fillStyle = lastBackgroundColor;
        }
        ctx.beginPath();
        ctx.moveTo(quad.p1[0], quad.p1[1]);
        ctx.lineTo(quad.p2[0], quad.p2[1]);
        ctx.lineTo(quad.p3[0], quad.p3[1]);
        ctx.lineTo(quad.p4[0], quad.p4[1]);
        ctx.closePath();
        ctx.fill();
      }

      // Outlines.
      for (var i = 0; i < quads.length; i++) {
        var quad = quads[i];
        ctx.beginPath();
        ctx.moveTo(quad.p1[0], quad.p1[1]);
        ctx.lineTo(quad.p2[0], quad.p2[1]);
        ctx.lineTo(quad.p3[0], quad.p3[1]);
        ctx.lineTo(quad.p4[0], quad.p4[1]);
        ctx.closePath();
        if (quad.borderColor)
          ctx.strokeStyle = quad.borderColor;
        else
          ctx.strokeStyle = 'rgb(128,128,128)';
        ctx.stroke();
      }

      // Selection outlines.
      ctx.lineWidth = vp.getDeviceLineWidthAssumingTransformIsApplied(8.0);
      var rules = window.getMatchedCSSRules(this.canvas_);

      // TODO(nduca): Figure out how to get these from css.
      if (document.activeElement == this.canvas_)
        ctx.strokeStyle = 'rgb(187,226,54)';
      else
        ctx.strokeStyle = 'rgb(156,189,45)';

      for (var i = 0; i < quads.length; i++) {
        var quad = quads[i];
        if (!quad.selected)
          continue;
        ctx.beginPath();
        ctx.moveTo(quad.p1[0], quad.p1[1]);
        ctx.lineTo(quad.p2[0], quad.p2[1]);
        ctx.lineTo(quad.p3[0], quad.p3[1]);
        ctx.lineTo(quad.p4[0], quad.p4[1]);
        ctx.closePath();
        ctx.stroke();
      }

      if (this.deviceViewportSizeForFrame_) {
        ctx.lineWidth = vp.getDeviceLineWidthAssumingTransformIsApplied(3.0);
        ctx.strokeStyle = 'rgba(0,0,255,1)';
        ctx.strokeRect(0,
                       0,
                       this.deviceViewportSizeForFrame_.width,
                       this.deviceViewportSizeForFrame_.height);
      }

      ctx.restore();
    },

    selectQuadsAtCanvasClientPoint: function(clientX, clientY) {
      var selectedQuadIndices = this.findQuadsAtCanvasClientPoint(
          clientX, clientY);
      var e = new base.Event('selectionChanged');
      e.selectedQuadIndices = selectedQuadIndices;
      this.dispatchEvent(e);
      this.viewport_.forceRedrawAll();
    },

    findQuadsAtCanvasClientPoint: function(clientX, clientY) {
      var bounds = this.canvas_.getBoundingClientRect();
      var vecInLayout = vec2.createXY(clientX - bounds.left,
                                      clientY - bounds.top);
      var vecInWorldPixels =
          this.viewport_.layoutPixelsToWorldPixels2(vecInLayout);

      var quads = this.quads_;
      var hitIndices = [];
      for (var i = 0; i < quads.length; i++) {
        var hit = quads[i].vecInside(vecInWorldPixels);
        if (hit)
          hitIndices.push(i);
      }
      return hitIndices;
    },

    onMouseDown_: function(e) {
      this.selectQuadsAtCanvasClientPoint(e.clientX, e.clientY);
      document.addEventListener('mousemove', this.onMouseMove_);
      document.addEventListener('mouseup', this.onMouseUp_);
      e.preventDefault();
      this.canvas_.focus();
      return true;
    },

    onMouseMove_: function(e) {
      this.selectQuadsAtCanvasClientPoint(e.clientX, e.clientY);
    },

    onMouseUp_: function(e) {
      this.selectQuadsAtCanvasClientPoint(e.clientX, e.clientY);
      document.removeEventListener('mousemove', this.onMouseMove_);
      document.removeEventListener('mouseup', this.onMouseUp_);
    }

  };

  return {
    QuadView: QuadView
  };
});
<|endoftext|>


<commit_before>
'use strict';

/**
 * Petition information loaded from the data source.
 * @namespace Petitions
 */

/**
 * Government response to the petition.
 * @typedef {object} Petitions.Response
 * @property {string} summary - Summary of the response.
 * @property {string} details - Detailed response.
 * @property {string} created_at - The timestamp the government responded.
 * @property {string} updated_at - The timestamp the government last updated the response.
 */

/**
 * Summary of information about the petition.
 * @typedef {object} Petitions.Summary
 * @property {string} action - The call to action.
 * @property {string} background - The background to the petition.
 * @property {string} additional_details - The additional details about petition.
 * @property {number} signature_count - The number of signatures.
 * @property {string} state - The state of the petition.
 * @property {string} created_at - The timestamp petition was created.
 * @property {string} government_response_at - The timestamp the government responded.
 * @property {string} response_threshold_reached_at - The timestamp 10000 signatures was reached.
 * @property {string} debate_threshold_reached_at - The timestamp 100000 signatures was reached.
 * @property {string} scheduled_debate_date - The date the debate has been scheduled for.
 */

/**
 * Breakdown of signatures by country.
 * @typedef {object} Petitions.SignaturesByCountry
 * @property {string} name - Name of the country.
 * @property {number} signature_count - The number of signatures from that country.
 */

/**
 * Breakdown of signatures by constituency.
 * @typedef {object} Petitions.SignaturesByConstituency
 * @property {string} name - Name of the constituency.
 * @property {string} ons_code - The ONS code.
 * @property {string} mp - The name of the MP for the constituency.
 * @property {number} signature_count - The number of signatures from that constituency.
 */

/**
 * Detailed information about the petition.
 * @typedef {object} Petitions.Detail
 * @property {string} action - The call to action.
 * @property {string} background - The background to the petition.
 * @property {string} additional_details - The additional details about petition.
 * @property {number} signature_count - The number of signatures.
 * @property {string} state - The state of the petition.
 * @property {string} created_at - The timestamp petition was created.
 * @property {string} government_response_at - The timestamp the government responded.
 * @property {string} response_threshold_reached_at - The timestamp 10000 signatures was reached.
 * @property {string} debate_threshold_reached_at - The timestamp 100000 signatures was reached.
 * @property {string} scheduled_debate_date - The date the debate has been scheduled for.
 * @property {Petitions.SignaturesByCountry} signatures_by_country - The signatures by country.
 * @property {Petitions.SignaturesByConstituency} signatures_by_constituency - The signatures by constituency.
 */

/**
 * Representation of a petition.
 * @typedef {object} Petitions.Petition
 * @property {number} id - The id of the petition.
 * @property {Petitions.Summary|Petitions.Detail} attributes - The information known about the petition.
 */

/**
 * The petitions module.
 * @module petitions
 * @author Matt Champion
 */
module.exports = {
    PetitionsMonitor : require('../target/js/public/petitions-monitor').UkPetitions,
    PetitionLoader : require('../target/js/public/petition-loader').UkPetitions,
    PetitionPageLoader : require('../target/js/public/petition-page-loader').UkPetitions,
    PetitionPager : require('../target/js/public/petition-pager').UkPetitions,
    queries : require('../target/js/public/petition-queries').UkPetitions,
    output : require('../target/js/public/simple-output').UkPetitions
};
<commit_msg>Export classes correctly.<commit_after>
'use strict';

/**
 * Petition information loaded from the data source.
 * @namespace Petitions
 */

/**
 * Government response to the petition.
 * @typedef {object} Petitions.Response
 * @property {string} summary - Summary of the response.
 * @property {string} details - Detailed response.
 * @property {string} created_at - The timestamp the government responded.
 * @property {string} updated_at - The timestamp the government last updated the response.
 */

/**
 * Summary of information about the petition.
 * @typedef {object} Petitions.Summary
 * @property {string} action - The call to action.
 * @property {string} background - The background to the petition.
 * @property {string} additional_details - The additional details about petition.
 * @property {number} signature_count - The number of signatures.
 * @property {string} state - The state of the petition.
 * @property {string} created_at - The timestamp petition was created.
 * @property {string} government_response_at - The timestamp the government responded.
 * @property {string} response_threshold_reached_at - The timestamp 10000 signatures was reached.
 * @property {string} debate_threshold_reached_at - The timestamp 100000 signatures was reached.
 * @property {string} scheduled_debate_date - The date the debate has been scheduled for.
 */

/**
 * Breakdown of signatures by country.
 * @typedef {object} Petitions.SignaturesByCountry
 * @property {string} name - Name of the country.
 * @property {number} signature_count - The number of signatures from that country.
 */

/**
 * Breakdown of signatures by constituency.
 * @typedef {object} Petitions.SignaturesByConstituency
 * @property {string} name - Name of the constituency.
 * @property {string} ons_code - The ONS code.
 * @property {string} mp - The name of the MP for the constituency.
 * @property {number} signature_count - The number of signatures from that constituency.
 */

/**
 * Detailed information about the petition.
 * @typedef {object} Petitions.Detail
 * @property {string} action - The call to action.
 * @property {string} background - The background to the petition.
 * @property {string} additional_details - The additional details about petition.
 * @property {number} signature_count - The number of signatures.
 * @property {string} state - The state of the petition.
 * @property {string} created_at - The timestamp petition was created.
 * @property {string} government_response_at - The timestamp the government responded.
 * @property {string} response_threshold_reached_at - The timestamp 10000 signatures was reached.
 * @property {string} debate_threshold_reached_at - The timestamp 100000 signatures was reached.
 * @property {string} scheduled_debate_date - The date the debate has been scheduled for.
 * @property {Petitions.SignaturesByCountry} signatures_by_country - The signatures by country.
 * @property {Petitions.SignaturesByConstituency} signatures_by_constituency - The signatures by constituency.
 */

/**
 * Representation of a petition.
 * @typedef {object} Petitions.Petition
 * @property {number} id - The id of the petition.
 * @property {Petitions.Summary|Petitions.Detail} attributes - The information known about the petition.
 */

/**
 * The petitions module.
 * @module petitions
 * @author Matt Champion
 */
module.exports = {
    PetitionsMonitor : require('../target/js/public/petitions-monitor').UkPetitions.PetitionsMonitor,
    PetitionLoader : require('../target/js/public/petition-loader').UkPetitions.PetitionLoader,
    PetitionPageLoader : require('../target/js/public/petition-page-loader').UkPetitions.PetitionPageLoader,
    PetitionPager : require('../target/js/public/petition-pager').UkPetitions.PetitionPager,
    queries : require('../target/js/public/petition-queries').UkPetitions,
    output : require('../target/js/public/simple-output').UkPetitions
};
<|endoftext|>


<commit_before>const uncapitalize = (a) => {
  return a.replace(/(^[A-Z])(.+)/, function(match, a, b) {
    return a.toLowerCase() + b;
  });
};

export default uncapitalize;
<commit_msg>function to =><commit_after>const uncapitalize = (a) => {
  return a.replace(/(^[A-Z])(.+)/, (match, a, b) => {
    return a.toLowerCase() + b;
  });
};

export default uncapitalize;
<|endoftext|>


<commit_before>/**
 * util-request.js - The utilities for requesting script and style files
 * ref: tests/research/load-js-css/test.html
 */

var head = doc.getElementsByTagName("head")[0] || doc.documentElement
var baseElement = head.getElementsByTagName("base")[0]

var IS_CSS_RE = /\.css(?:\?|$)/i
var READY_STATE_RE = /^(?:loaded|complete|undefined)$/

var currentlyAddingScript
var interactiveScript

// `onload` event is supported in WebKit < 535.23 and Firefox < 9.0
// ref:
//  - https://bugs.webkit.org/show_activity.cgi?id=38995
//  - https://bugzilla.mozilla.org/show_bug.cgi?id=185236
//  - https://developer.mozilla.org/en/HTML/Element/link#Stylesheet_load_events
var isOldWebKit = (navigator.userAgent
    .replace(/.*AppleWebKit\/(\d+)\..*/, "$1")) * 1 < 536

function request(url, callback, charset) {
  var isCSS = IS_CSS_RE.test(url)
  var node = doc.createElement(isCSS ? "link" : "script")

  if (charset) {
    var cs = isFunction(charset) ? charset(url) : charset
    if (cs) {
      node.charset = cs
    }
  }

  addOnload(node, callback, isCSS)

  if (isCSS) {
    node.rel = "stylesheet"
    node.href = url
  }
  else {
    node.async = true
    node.src = url
  }

  // For some cache cases in IE 6-8, the script executes IMMEDIATELY after
  // the end of the insert execution, so use `currentlyAddingScript` to
  // hold current node, for deriving url in `define` call
  currentlyAddingScript = node

  // ref: #185 & http://dev.jquery.com/ticket/2709
  baseElement ?
      head.insertBefore(node, baseElement) :
      head.appendChild(node)

  currentlyAddingScript = null
}

function addOnload(node, callback, isCSS) {
  var missingOnload = isCSS && (isOldWebKit || !("onload" in node))

  // for Old WebKit and Old Firefox
  if (missingOnload) {
    setTimeout(function() {
      pollCss(node, callback)
    }, 1) // Begin after node insertion
    return
  }

  node.onload = node.onerror = node.onreadystatechange = function() {
    if (READY_STATE_RE.test(node.readyState)) {

      // Ensure only run once and handle memory leak in IE
      node.onload = node.onerror = node.onreadystatechange = null

      // Remove the script to reduce memory leak
      if (!isCSS && !data.debug) {
        head.removeChild(node)
      }

      // Dereference the node
      node = null

      callback()
    }
  }
}

function pollCss(node, callback) {
  var sheet = node.sheet
  var isLoaded

  // for WebKit < 536
  if (isOldWebKit) {
    if (sheet) {
      isLoaded = true
    }
  }
  // for Firefox < 9.0
  else if (sheet) {
    try {
      if (sheet.cssRules) {
        isLoaded = true
      }
    } catch (ex) {
      // The value of `ex.name` is changed from "NS_ERROR_DOM_SECURITY_ERR"
      // to "SecurityError" since Firefox 13.0. But Firefox is less than 9.0
      // in here, So it is ok to just rely on "NS_ERROR_DOM_SECURITY_ERR"
      if (ex.name === "NS_ERROR_DOM_SECURITY_ERR") {
        isLoaded = true
      }
    }
  }

  setTimeout(function() {
    if (isLoaded) {
      // Place callback here to give time for style rendering
      callback()
    }
    else {
      pollCss(node, callback)
    }
  }, 20)
}

function getCurrentScript() {
  if (currentlyAddingScript) {
    return currentlyAddingScript
  }

  // For IE6-9 browsers, the script onload event may not fire right
  // after the the script is evaluated. Kris Zyp found that it
  // could query the script nodes and the one that is in "interactive"
  // mode indicates the current script
  // ref: http://goo.gl/JHfFW
  if (interactiveScript && interactiveScript.readyState === "interactive") {
    return interactiveScript
  }

  var scripts = head.getElementsByTagName("script")

  for (var i = scripts.length - 1; i >= 0; i--) {
    var script = scripts[i]
    if (script.readyState === "interactive") {
      interactiveScript = script
      return interactiveScript
    }
  }
}

<commit_msg>Update util-request.js<commit_after>/**
 * util-request.js - The utilities for requesting script and style files
 * ref: tests/research/load-js-css/test.html
 */

var head = doc.getElementsByTagName("head")[0] || doc.documentElement
var baseElement = head.getElementsByTagName("base")[0]

var IS_CSS_RE = /\.css(?:\?|$)/i
var READY_STATE_RE = /^(?:loaded|complete|undefined)$/

var currentlyAddingScript
var interactiveScript

// `onload` event is supported in WebKit < 535.23 and Firefox < 9.0
// ref:
//  - https://bugs.webkit.org/show_activity.cgi?id=38995
//  - https://bugzilla.mozilla.org/show_bug.cgi?id=185236
//  - https://developer.mozilla.org/en/HTML/Element/link#Stylesheet_load_events
var isOldWebKit = (navigator.userAgent
    .replace(/.*AppleWebKit\/(\d+)\..*/, "$1")) * 1 < 536

function request(url, callback, charset) {
  var isCSS = IS_CSS_RE.test(url)
  var node = doc.createElement(isCSS ? "link" : "script")

  if (charset) {
    var cs = isFunction(charset) ? charset(url) : charset
    if (cs) {
      node.charset = cs
    }
  }

  addOnload(node, callback, isCSS)

  if (isCSS) {
    node.rel = "stylesheet"
    node.href = url
  }
  else {
    node.async = true
    node.src = url
  }

  // For some cache cases in IE 6-8, the script executes IMMEDIATELY after
  // the end of the insert execution, so use `currentlyAddingScript` to
  // hold current node, for deriving url in `define` call
  currentlyAddingScript = node

  // ref: #185 & http://dev.jquery.com/ticket/2709
  baseElement ?
      head.insertBefore(node, baseElement) :
      head.appendChild(node)

  currentlyAddingScript = null
}

function addOnload(node, callback, isCSS) {
  var missingOnload = isCSS && (isOldWebKit || !("onload" in node))

  // for Old WebKit and Old Firefox
  if (missingOnload) {
    setTimeout(function() {
      pollCss(node, callback)
    }, 1) // Begin after node insertion
    return
  }

  node.onload = node.onerror = node.onreadystatechange = function() {
    if (READY_STATE_RE.test(node.readyState)) {

      // Ensure only run once and handle memory leak in IE
      node.onload = node.onerror = node.onreadystatechange = null

      // Remove the script to reduce memory leak
      if (!isCSS && !data.debug) {
        head.removeChild(node)
      }

      // Dereference the node
      node = null

      callback()
    }
  }
}

function pollCss(node, callback) {
  var sheet = node.sheet
  var isLoaded

  // for WebKit < 536
  if (isOldWebKit) {
    if (sheet) {
      isLoaded = true
    }
  }
  // for Firefox < 9.0
  else if (sheet) {
    try {
      if (sheet.cssRules) {
        isLoaded = true
      }
    } catch (ex) {
      // The value of `ex.name` is changed from "NS_ERROR_DOM_SECURITY_ERR"
      // to "SecurityError" since Firefox 13.0. But Firefox is less than 9.0
      // in here, So it is ok to just rely on "NS_ERROR_DOM_SECURITY_ERR"
      if (ex.name === "NS_ERROR_DOM_SECURITY_ERR") {
        isLoaded = true
      }
    }
  }

  setTimeout(function() {
    if (isLoaded) {
      // Place callback here to give time for style rendering
      callback()
    }
    else {
      pollCss(node, callback)
    }
  }, 20)
}

function getCurrentScript() {
  if (currentlyAddingScript) {
    return currentlyAddingScript
  }

  // For IE6-9 browsers, the script onload event may not fire right
  // after the script is evaluated. Kris Zyp found that it
  // could query the script nodes and the one that is in "interactive"
  // mode indicates the current script
  // ref: http://goo.gl/JHfFW
  if (interactiveScript && interactiveScript.readyState === "interactive") {
    return interactiveScript
  }

  var scripts = head.getElementsByTagName("script")

  for (var i = scripts.length - 1; i >= 0; i--) {
    var script = scripts[i]
    if (script.readyState === "interactive") {
      interactiveScript = script
      return interactiveScript
    }
  }
}

<|endoftext|>