<commit_before>/* global location: true, escape: true, XMLHttpRequest: true */
define(function(require) {
    'use strict';

    var Ember = require('ember'),
        meta = require('meta-data'),
        ytMp3 = require('helpers/yt-mp3'),
        utilities = require('helpers/utilities'),
        signateUrl,
        extractExtension,
        pluralizations;

    signateUrl = function(url) {
        var host = 'http://www.youtube-mp3.org';

        return meta.downloadHost + url + '&s=' + ytMp3.createSignature(host + url);
    };

    extractExtension = function(source) {
        return source.substr(source.lastIndexOf('.') + 1, source.length);
    };

    // TODO: Rename audio?
    pluralizations = {
        audio: 'audio',
        thumbnail: 'thumbnails'
    };

    return Ember.Object.extend({
        init: function() {
            this._super();

            this.set('labels', []);
        },
        id: null,
        name: null,
        extension: null,
        labels: null,
        audio: null,
        thumbnail: null,
        fileSystem: null,
        isSelected: false,
        status: null,
        isOffline: function() {
            return this.get('fileSystem.snippets').isAny('id', this.get('id'));
        }.property('fileSystem.snippets.@each'),
        isDownloading: function() {
            return this.get('status') === 'downloading';
        }.property('status'),
        isPlaying: function() {
            return this.get('status') === 'playing';
        }.property('status'),
        isDownloaded: function() {
            return this.get('labels').contains('downloaded');
        }.property('labels.@each'),
        isQueued: function() {
            return this.get('fileSystem.queue').contains(this.get('id'));
        }.property('fileSystem.queue.@each', 'id'),
        createFilePath: function(type, extension) {
            var fileName = this.get('id') + '.' + extension,
                directory = pluralizations[type];

            return directory + '/' + fileName;
        },
        fetchDownload: function() {
            var videoUrl = 'http://www.youtube.com/watch?v=' + this.get('id'),
                url;

            url = '/a/pushItem/?';
            url += 'item=' + escape(videoUrl);
            url += '&el=na&bf=false';
            url += '&r=' + (new Date).getTime();

            return Ember.$.ajax(signateUrl(url)).then(function(videoId) {
                url = '/a/itemInfo/?';
                url += 'video_id=' + videoId;
                url += '&ac=www&t=grp';
                url += '&r=' + (new Date).getTime();

                return Ember.$.ajax(signateUrl(url)).then(function(info) {
                    info = info.substring(7, info.length - 1);
                    info = JSON.parse(info);

                    url = '/get?';
                    url += 'video_id=' + videoId;
                    url += '&ts_create=' + info.ts_create;
                    url += '&r=' + info.r;
                    url += '&h2=' + info.h2;

                    this.set('audio', signateUrl(url));

                    return this.get('audio');
                }.bind(this));
            }.bind(this));
        },
        download: function() {
            this.set('status', 'downloading');

            return new Ember.RSVP.Promise(function(resolve, reject) {
                if (Ember.isEmpty(this.get('audio'))) {
                    this.fetchDownload().then(function(url) {
                        this.insert().then(resolve, reject);
                    }.bind(this));
                } else {
                    this.insert().then(resolve, reject);
                }
            }.bind(this));
        },
        insert: function() {
            var audio = this.createFilePath('audio', this.get('extension')),
                thumbnail = this.createFilePath('thumbnail', extractExtension(this.get('thumbnail'))),
                promises;

            promises = {
                // TODO: No 'Access-Control-Allow-Origin' header because the requested URL redirects to another domain
                audio: this._download(this.get('audio'), audio),
                // TODO: write to filesystem on snippet property change
                thumbnail: this._download(this.get('thumbnail'), thumbnail)
            };

            return new Ember.RSVP.Promise(function(resolve, reject) {
                Ember.RSVP.hash(promises).then(function(hash) {
                    this.set('audio', hash.audio);
                    this.set('thumbnail', hash.thumbnail);

                    this.get('labels').pushObject('downloaded');

                    // TODO: update offline labels and snippets in 1 write action
                    // TODO: only perform this
                    this.get('fileSystem.snippets').pushObject(this);

                    this.set('status', null);

                    resolve();
                }.bind(this), function(reason) {
                    reject(reason.message);
                });
            }.bind(this));
        },
        _download: function(url, source) {
            var fileSystem = this.get('fileSystem'),
                xhr = new XMLHttpRequest(),
                response;

            xhr.open('GET', url, true);
            xhr.responseType = 'arraybuffer';

            return new Ember.RSVP.Promise(function(resolve) {
                xhr.onload = function() {
                    response = this.response;

                    fileSystem.get('instance').root.getFile(source, {
                        create: true
                    }, function(fileEntry) {
                        fileEntry.createWriter(function(fileWriter) {
                            fileWriter.onwriteend = function() {
                                resolve(fileEntry.toURL());
                            };

                            fileWriter.write(new Blob([response]));
                        });
                    });
                };

                xhr.send();
            });
        },
        remove: function() {
            var fileSystem = this.get('fileSystem'),
                promises;

            promises = {
                audio: fileSystem.remove(this.get('audio')),
                thumbnail: fileSystem.remove(this.get('thumbnail'))
            };

            return new Ember.RSVP.Promise(function(resolve, reject) {
                Ember.RSVP.all(promises).then(function() {
                    fileSystem.get('snippets').removeObject(this);

                    resolve();
                }.bind(this), reject);
            });
        },
        strip: function() {
            return this.getProperties('id', 'name', 'extension', 'labels', 'thumbnail');
        }
    });
});
<commit_msg>Fixed bug<commit_after>/* global location: true, escape: true, XMLHttpRequest: true */
define(function(require) {
    'use strict';

    var Ember = require('ember'),
        meta = require('meta-data'),
        ytMp3 = require('helpers/yt-mp3'),
        utilities = require('helpers/utilities'),
        signateUrl,
        extractExtension,
        pluralizations;

    signateUrl = function(url) {
        var host = 'http://www.youtube-mp3.org';

        return meta.downloadHost + url + '&s=' + ytMp3.createSignature(host + url);
    };

    extractExtension = function(source) {
        return source.substr(source.lastIndexOf('.') + 1, source.length);
    };

    // TODO: Rename audio?
    pluralizations = {
        audio: 'audio',
        thumbnail: 'thumbnails'
    };

    return Ember.Object.extend({
        init: function() {
            this._super();

            this.set('labels', []);
        },
        id: null,
        name: null,
        extension: null,
        labels: null,
        audio: null,
        thumbnail: null,
        fileSystem: null,
        isSelected: false,
        status: null,
        isOffline: function() {
            return this.get('fileSystem.snippets').isAny('id', this.get('id'));
        }.property('fileSystem.snippets.@each'),
        isDownloading: function() {
            return this.get('status') === 'downloading';
        }.property('status'),
        isPlaying: function() {
            return this.get('status') === 'playing';
        }.property('status'),
        isDownloaded: function() {
            return this.get('labels').contains('downloaded');
        }.property('labels.@each'),
        isQueued: function() {
            return this.get('fileSystem.queue').contains(this.get('id'));
        }.property('fileSystem.queue.@each', 'id'),
        createFilePath: function(type, extension) {
            var fileName = this.get('id') + '.' + extension,
                directory = pluralizations[type];

            return directory + '/' + fileName;
        },
        fetchDownload: function() {
            var videoUrl = 'http://www.youtube.com/watch?v=' + this.get('id'),
                url;

            url = '/a/pushItem/?';
            url += 'item=' + escape(videoUrl);
            url += '&el=na&bf=false';
            url += '&r=' + (new Date).getTime();

            return Ember.$.ajax(signateUrl(url)).then(function(videoId) {
                url = '/a/itemInfo/?';
                url += 'video_id=' + videoId;
                url += '&ac=www&t=grp';
                url += '&r=' + (new Date).getTime();

                return Ember.$.ajax(signateUrl(url)).then(function(info) {
                    info = info.substring(7, info.length - 1);
                    info = JSON.parse(info);

                    url = '/get?';
                    url += 'video_id=' + videoId;
                    url += '&ts_create=' + info.ts_create;
                    url += '&r=' + info.r;
                    url += '&h2=' + info.h2;

                    this.set('audio', signateUrl(url));

                    return this.get('audio');
                }.bind(this));
            }.bind(this));
        },
        download: function() {
            this.set('status', 'downloading');

            return new Ember.RSVP.Promise(function(resolve, reject) {
                if (Ember.isEmpty(this.get('audio'))) {
                    this.fetchDownload().then(function(url) {
                        this.insert().then(resolve, reject);
                    }.bind(this));
                } else {
                    this.insert().then(resolve, reject);
                }
            }.bind(this));
        },
        insert: function() {
            var audio = this.createFilePath('audio', this.get('extension')),
                thumbnail = this.createFilePath('thumbnail', extractExtension(this.get('thumbnail'))),
                promises;

            promises = {
                // TODO: No 'Access-Control-Allow-Origin' header because the requested URL redirects to another domain
                audio: this._download(this.get('audio'), audio),
                // TODO: write to filesystem on snippet property change
                thumbnail: this._download(this.get('thumbnail'), thumbnail)
            };

            return new Ember.RSVP.Promise(function(resolve, reject) {
                Ember.RSVP.hash(promises).then(function(hash) {
                    this.set('audio', hash.audio);
                    this.set('thumbnail', hash.thumbnail);

                    this.get('labels').pushObject('downloaded');

                    // TODO: update offline labels and snippets in 1 write action
                    // TODO: only perform this
                    if (!this.get('isOffline')) {
                        this.get('fileSystem.snippets').pushObject(this);
                    }

                    this.set('status', null);

                    resolve();
                }.bind(this), function(reason) {
                    reject(reason.message);
                });
            }.bind(this));
        },
        _download: function(url, source) {
            var fileSystem = this.get('fileSystem'),
                xhr = new XMLHttpRequest(),
                response;

            xhr.open('GET', url, true);
            xhr.responseType = 'arraybuffer';

            return new Ember.RSVP.Promise(function(resolve) {
                xhr.onload = function() {
                    response = this.response;

                    fileSystem.get('instance').root.getFile(source, {
                        create: true
                    }, function(fileEntry) {
                        fileEntry.createWriter(function(fileWriter) {
                            fileWriter.onwriteend = function() {
                                resolve(fileEntry.toURL());
                            };

                            fileWriter.write(new Blob([response]));
                        });
                    });
                };

                xhr.send();
            });
        },
        remove: function() {
            var fileSystem = this.get('fileSystem'),
                promises;

            promises = {
                audio: fileSystem.remove(this.get('audio')),
                thumbnail: fileSystem.remove(this.get('thumbnail'))
            };

            return new Ember.RSVP.Promise(function(resolve, reject) {
                Ember.RSVP.all(promises).then(function() {
                    fileSystem.get('snippets').removeObject(this);

                    resolve();
                }.bind(this), reject);
            });
        },
        strip: function() {
            return this.getProperties('id', 'name', 'extension', 'labels', 'thumbnail');
        }
    });
});
<|endoftext|>


<commit_before>

function LayerDefinition(layerDefinition, options) {

  this.options = _.defaults(options, {
    ajax: $.ajax,
    pngParams: ['map_key', 'api_key', 'cache_policy', 'updated_at'],
    gridParams: ['map_key', 'api_key', 'cache_policy', 'updated_at'],
    cors: this.isCORSSupported(),
    btoa: this.isBtoaSupported() ? this._encodeBase64Native : this._encodeBase64
  });

  this.setLayerDefinition(layerDefinition, { silent: true });
  this.layerToken = null;
  this.urls = null;
  this.silent = false;
  this._layerTokenQueue = [];
  this._timeout = -1;
}

LayerDefinition.prototype = {

  /*
   * TODO: extract these two functions to some core module
   */
  isCORSSupported: function() {
    return 'withCredentials' in new XMLHttpRequest() || typeof XDomainRequest !== "undefined";
  },

  isBtoaSupported: function() {
    return typeof window['btoa'] == 'function';
  },

  getLayerCount: function() {
    return this.layers.length;
  },

  setLayerDefinition: function(layerDefinition, options) {
    options = options || {};
    this.version = layerDefinition.version || '1.0.0';
    this.stat_tag = layerDefinition.stat_tag;
    this.layers = _.clone(layerDefinition.layers);
    if(!options.silent) {
      this._definitionUpdated();
    }
  },

  toJSON: function() {
    var obj = {};
    obj.version = this.version;
    if(this.stat_tag) {
      obj.stat_tag = this.stat_tag;
    }
    obj.layers = [];
    for(var i in this.layers) {
      var layer = this.layers[i];
      obj.layers.push({
        type: 'cartodb',
        options: {
          sql: layer.options.sql,
          cartocss: layer.options.cartocss,
          cartocss_version: layer.options.cartocss_version || '2.1.0',
          interactivity: layer.options.interactivity
        }
      });
    }
    return obj;
  },

  _encodeBase64Native: function (input) {
    return btoa(input)
  },

  // ie7 btoa,
  // from http://phpjs.org/functions/base64_encode/
  _encodeBase64: function (data) {
    var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    var o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
      ac = 0,
      enc = "",
      tmp_arr = [];

    if (!data) {
      return data;
    }

    do { // pack three octets into four hexets
      o1 = data.charCodeAt(i++);
      o2 = data.charCodeAt(i++);
      o3 = data.charCodeAt(i++);

      bits = o1 << 16 | o2 << 8 | o3;

      h1 = bits >> 18 & 0x3f;
      h2 = bits >> 12 & 0x3f;
      h3 = bits >> 6 & 0x3f;
      h4 = bits & 0x3f;

      // use hexets to index into b64, and append result to encoded string
      tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
    } while (i < data.length);

    enc = tmp_arr.join('');

    var r = data.length % 3;
    return (r ? enc.slice(0, r - 3) : enc) + '==='.slice(r || 3);
  },

  _array2hex: function(byteArr) {
    var encoded = []
    for(var i = 0; i < byteArr.length; ++i) {
      encoded.push(String.fromCharCode(byteArr[i] + 128));
    }
    return this.options.btoa(encoded.join(''))
  },

  getLayerToken: function(callback) {
    var self = this;
    clearTimeout(this._timeout);
    this._layerTokenQueue.push(callback);
    this._timeout = setTimeout(function() {
      self._getLayerToken(function(data) {
        var fn;
        while(fn = self._layerTokenQueue.pop()) fn(data);
      });
    }, 4);
  },

  _getLayerToken: function(callback) {
    var ajax = this.options.ajax;
    if(this.options.cors) {
      ajax({
        crossOrigin: true,
        type: 'POST',
        dataType: 'json',
        contentType: 'application/json',
        url: this._tilerHost() + '/tiles/layergroup',
        data: JSON.stringify(this.toJSON()),
        success: function(data) {
          callback(data);
        },
        error: function() {
          callback(null);
        }
      });
    } else {
      var self = this;
      var json = '{ "config": "' +
        JSON.stringify(this.toJSON()).replace(/"/g, '\\"') +
        '"}';
      LZMA.compress(json, 3, function(encoded) {
        encoded = self._array2hex(encoded);
        ajax({
          dataType: 'jsonp',
          url: self._tilerHost() + '/tiles/layergroup?lzma=' + encodeURIComponent(encoded),
          success: function(data) {
            callback(data);
          },
          error: function() {
            callback(null);
          }
        });
      });
    }
    return this;
  },

  removeLayer: function(layer) {
    if(layer < this.getLayerCount() && layer >= 0) {
      this.layers.splice(layer, 1);
      this._definitionUpdated();
    }
    return this;
  },

  getLayer: function(index) {
    return _.clone(this.layers[index]);
  },

  invalidate: function() {
    this.layerToken = null;
    this.urls = null;
  },

  setLayer: function(layer, def) {
    if(layer < this.getLayerCount() && layer >= 0) {
      this.layers[layer] = _.clone(def);
    }
    return this;
  },

  addLayer: function(def, layer) {
    layer = layer === undefined ? this.getLayerCount(): layer;
    if(layer <= this.getLayerCount() && layer >= 0) {
      if(!def.sql || !def.cartocss) {
        throw new Error("layer definition should contain at least a sql and a cartocss");
        return this;
      }
      this.layers.splice(layer, 0, {
        type: 'cartodb',
        options: def
      });
      this._definitionUpdated();
    }
    return this;
  },

  getTiles: function(callback) {
    var self = this;
    if(self.layerToken) {
      callback && callback(self._layerGroupTiles(self.layerToken, self.options.extra_params));
      return this;
    }
    this.getLayerToken(function(data) {
      if(data) {
        self.layerToken = data.layergroupid;
        self.urls = self._layerGroupTiles(data.layergroupid, self.options.extra_params);
        callback && callback(self.urls);
      } else {
        callback && callback(null);
      }
    });
    return this;
  },

  isHttps: function() {
    return this.options.tiler_protocol === 'https';
  },

  _layerGroupTiles: function(layerGroupId, params) {
    var subdomains = this.options.subdomains || ['0', '1', '2', '3'];
    if(this.isHttps()) {
      subdomains = [null]; // no subdomain
    }

    var tileTemplate = '/{z}/{x}/{y}';

    var grids = []
    var tiles = [];

    var pngParams = this._encodeParams(params, this.options.pngParams);
    for(var i = 0; i < subdomains.length; ++i) {
      var s = subdomains[i]
      var cartodb_url = this._host(s) + '/tiles/layergroup/' + layerGroupId
      tiles.push(cartodb_url + tileTemplate + ".png?" + pngParams );

      var gridParams = this._encodeParams(params, this.options.gridParams);
      for(var layer in this.layers) {
        grids[layer] = grids[layer] || [];
        grids[layer].push(cartodb_url + "/" + layer +  tileTemplate + ".grid.json?" + gridParams);
      }
    }

    return {
      tiles: tiles,
      grids: grids
    }

  },

  /**
   * set interactivity attributes for a layer.
   * if attributes are passed as first param layer 0 is
   * set
   */
  setInteractivity: function(layer, attributes) {

    if(attributes === undefined) {
      attributes = layer;
      layer = 0;
    }

    if(typeof(attributes) == 'string') {
      attributes = attributes.split(',');
    }

    for(var i = 0; i < attributes.length; ++i) {
      attributes[i] = attributes[i].replace(/ /g, '');
    }

    this.layers[layer].options.interactivity = attributes;
    this._definitionUpdated();
    return this;
  },

  onLayerDefinitionUpdated: function() {},

  setSilent: function(b) {
    this.silent = b;
  },

  _definitionUpdated: function() {
    if(this.silent) return;
    this.invalidate();
    this.onLayerDefinitionUpdated();
  },

  _tileJSONfromTiles: function(layer, urls) {
    return {
      tilejson: '2.0.0',
      scheme: 'xyz',
      grids: urls.grids[layer],
      tiles: urls.tiles,
      formatter: function(options, data) { return data; }
     };
  },

  /**
   * get tile json for layer
   */
  getTileJSON: function(layer, callback) {
    layer = layer == undefined ? 0: layer;
    this.getTiles(function(urls) {
      if(!urls) {
        callback(null);
        return;
      }
      if(callback) {
        callback(this._tileJSONfromTiles(layer, urls));
      }
    });
  },

  /**
   * Change query of the tiles
   * @params {str} New sql for the tiles
   */
  setQuery: function(layer, sql) {
    if(sql === undefined) {
      sql = layer;
      layer = 0;
    }
    this.layers[layer].options.sql = sql
    this._definitionUpdated();
  },

  getQuery: function(layer) {
    layer = layer || 0;
    return this.layers[layer].options.sql
  },

  /**
   * Change style of the tiles
   * @params {style} New carto for the tiles
   */
  setCartoCSS: function(layer, style, version) {
    if(version === undefined) {
      version = style;
      style = layer;
      layer = 0;
    }

    version = version || cdb.CARTOCSS_DEFAULT_VERSION;

    this.layers[layer].options.cartocss = style;
    this.layers[layer].options.cartocss_version = version;
    this._definitionUpdated();

  },

  _encodeParams: function(params, included) {
    if(!params) return '';
    var url_params = [];
    included = included || _.keys(params);
    for(var i in included) {
      var k = included[i]
      var p = params[k];
      if(p) {
        var q = encodeURIComponent(p);
        q = q.replace(/%7Bx%7D/g,"{x}").replace(/%7By%7D/g,"{y}").replace(/%7Bz%7D/g,"{z}");
        url_params.push(k + "=" + q);
      }
    }
    return url_params.join('&')
  },

  _tilerHost: function() {
    var opts = this.options;
    return opts.tiler_protocol +
         "://" + ((opts.user_name) ? opts.user_name+".":"")  +
         opts.tiler_domain +
         ((opts.tiler_port != "") ? (":" + opts.tiler_port) : "");
  },

  _host: function(subhost) {
    var opts = this.options;
    if (opts.no_cdn) {
      return this._tilerHost();
    } else {
      var h = opts.tiler_protocol + "://";
      if (subhost) {
        h += subhost + ".";
      }
      h += cdb.CDB_HOST[opts.tiler_protocol] + "/" + opts.user_name;
      return h;
    }
  },

  getInfowindowData: function(layer) {
    return this.options.layer_definition.layers[layer].infowindow;
  },

  containInfowindow: function() {
    var layers =  this.options.layer_definition.layers;
    for(var i = 0; i < layers.length; ++i) {
      var infowindow = layers[i].infowindow;
      if (infowindow && infowindow.fields && infowindow.fields.length > 0) {
        return true;
      }
    }
    return false
  }

};
<commit_msg>invalidate now refresh the layer<commit_after>

function LayerDefinition(layerDefinition, options) {

  this.options = _.defaults(options, {
    ajax: $.ajax,
    pngParams: ['map_key', 'api_key', 'cache_policy', 'updated_at'],
    gridParams: ['map_key', 'api_key', 'cache_policy', 'updated_at'],
    cors: this.isCORSSupported(),
    btoa: this.isBtoaSupported() ? this._encodeBase64Native : this._encodeBase64
  });

  this.setLayerDefinition(layerDefinition, { silent: true });
  this.layerToken = null;
  this.urls = null;
  this.silent = false;
  this._layerTokenQueue = [];
  this._timeout = -1;
}

LayerDefinition.prototype = {

  /*
   * TODO: extract these two functions to some core module
   */
  isCORSSupported: function() {
    return 'withCredentials' in new XMLHttpRequest() || typeof XDomainRequest !== "undefined";
  },

  isBtoaSupported: function() {
    return typeof window['btoa'] == 'function';
  },

  getLayerCount: function() {
    return this.layers.length;
  },

  setLayerDefinition: function(layerDefinition, options) {
    options = options || {};
    this.version = layerDefinition.version || '1.0.0';
    this.stat_tag = layerDefinition.stat_tag;
    this.layers = _.clone(layerDefinition.layers);
    if(!options.silent) {
      this._definitionUpdated();
    }
  },

  toJSON: function() {
    var obj = {};
    obj.version = this.version;
    if(this.stat_tag) {
      obj.stat_tag = this.stat_tag;
    }
    obj.layers = [];
    for(var i in this.layers) {
      var layer = this.layers[i];
      obj.layers.push({
        type: 'cartodb',
        options: {
          sql: layer.options.sql,
          cartocss: layer.options.cartocss,
          cartocss_version: layer.options.cartocss_version || '2.1.0',
          interactivity: layer.options.interactivity
        }
      });
    }
    return obj;
  },

  _encodeBase64Native: function (input) {
    return btoa(input)
  },

  // ie7 btoa,
  // from http://phpjs.org/functions/base64_encode/
  _encodeBase64: function (data) {
    var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    var o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
      ac = 0,
      enc = "",
      tmp_arr = [];

    if (!data) {
      return data;
    }

    do { // pack three octets into four hexets
      o1 = data.charCodeAt(i++);
      o2 = data.charCodeAt(i++);
      o3 = data.charCodeAt(i++);

      bits = o1 << 16 | o2 << 8 | o3;

      h1 = bits >> 18 & 0x3f;
      h2 = bits >> 12 & 0x3f;
      h3 = bits >> 6 & 0x3f;
      h4 = bits & 0x3f;

      // use hexets to index into b64, and append result to encoded string
      tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
    } while (i < data.length);

    enc = tmp_arr.join('');

    var r = data.length % 3;
    return (r ? enc.slice(0, r - 3) : enc) + '==='.slice(r || 3);
  },

  _array2hex: function(byteArr) {
    var encoded = []
    for(var i = 0; i < byteArr.length; ++i) {
      encoded.push(String.fromCharCode(byteArr[i] + 128));
    }
    return this.options.btoa(encoded.join(''))
  },

  getLayerToken: function(callback) {
    var self = this;
    clearTimeout(this._timeout);
    this._layerTokenQueue.push(callback);
    this._timeout = setTimeout(function() {
      self._getLayerToken(function(data) {
        var fn;
        while(fn = self._layerTokenQueue.pop()) fn(data);
      });
    }, 4);
  },

  _getLayerToken: function(callback) {
    var ajax = this.options.ajax;
    if(this.options.cors) {
      ajax({
        crossOrigin: true,
        type: 'POST',
        dataType: 'json',
        contentType: 'application/json',
        url: this._tilerHost() + '/tiles/layergroup',
        data: JSON.stringify(this.toJSON()),
        success: function(data) {
          callback(data);
        },
        error: function() {
          callback(null);
        }
      });
    } else {
      var self = this;
      var json = '{ "config": "' +
        JSON.stringify(this.toJSON()).replace(/"/g, '\\"') +
        '"}';
      LZMA.compress(json, 3, function(encoded) {
        encoded = self._array2hex(encoded);
        ajax({
          dataType: 'jsonp',
          url: self._tilerHost() + '/tiles/layergroup?lzma=' + encodeURIComponent(encoded),
          success: function(data) {
            callback(data);
          },
          error: function() {
            callback(null);
          }
        });
      });
    }
    return this;
  },

  removeLayer: function(layer) {
    if(layer < this.getLayerCount() && layer >= 0) {
      this.layers.splice(layer, 1);
      this._definitionUpdated();
    }
    return this;
  },

  getLayer: function(index) {
    return _.clone(this.layers[index]);
  },

  invalidate: function() {
    this.layerToken = null;
    this.urls = null;
    this.onLayerDefinitionUpdated();
  },

  setLayer: function(layer, def) {
    if(layer < this.getLayerCount() && layer >= 0) {
      this.layers[layer] = _.clone(def);
    }
    return this;
  },

  addLayer: function(def, layer) {
    layer = layer === undefined ? this.getLayerCount(): layer;
    if(layer <= this.getLayerCount() && layer >= 0) {
      if(!def.sql || !def.cartocss) {
        throw new Error("layer definition should contain at least a sql and a cartocss");
        return this;
      }
      this.layers.splice(layer, 0, {
        type: 'cartodb',
        options: def
      });
      this._definitionUpdated();
    }
    return this;
  },

  getTiles: function(callback) {
    var self = this;
    if(self.layerToken) {
      callback && callback(self._layerGroupTiles(self.layerToken, self.options.extra_params));
      return this;
    }
    this.getLayerToken(function(data) {
      if(data) {
        self.layerToken = data.layergroupid;
        self.urls = self._layerGroupTiles(data.layergroupid, self.options.extra_params);
        callback && callback(self.urls);
      } else {
        callback && callback(null);
      }
    });
    return this;
  },

  isHttps: function() {
    return this.options.tiler_protocol === 'https';
  },

  _layerGroupTiles: function(layerGroupId, params) {
    var subdomains = this.options.subdomains || ['0', '1', '2', '3'];
    if(this.isHttps()) {
      subdomains = [null]; // no subdomain
    }

    var tileTemplate = '/{z}/{x}/{y}';

    var grids = []
    var tiles = [];

    var pngParams = this._encodeParams(params, this.options.pngParams);
    for(var i = 0; i < subdomains.length; ++i) {
      var s = subdomains[i]
      var cartodb_url = this._host(s) + '/tiles/layergroup/' + layerGroupId
      tiles.push(cartodb_url + tileTemplate + ".png?" + pngParams );

      var gridParams = this._encodeParams(params, this.options.gridParams);
      for(var layer in this.layers) {
        grids[layer] = grids[layer] || [];
        grids[layer].push(cartodb_url + "/" + layer +  tileTemplate + ".grid.json?" + gridParams);
      }
    }

    return {
      tiles: tiles,
      grids: grids
    }

  },

  /**
   * set interactivity attributes for a layer.
   * if attributes are passed as first param layer 0 is
   * set
   */
  setInteractivity: function(layer, attributes) {

    if(attributes === undefined) {
      attributes = layer;
      layer = 0;
    }

    if(typeof(attributes) == 'string') {
      attributes = attributes.split(',');
    }

    for(var i = 0; i < attributes.length; ++i) {
      attributes[i] = attributes[i].replace(/ /g, '');
    }

    this.layers[layer].options.interactivity = attributes;
    this._definitionUpdated();
    return this;
  },

  onLayerDefinitionUpdated: function() {},

  setSilent: function(b) {
    this.silent = b;
  },

  _definitionUpdated: function() {
    if(this.silent) return;
    this.invalidate();
  },

  _tileJSONfromTiles: function(layer, urls) {
    return {
      tilejson: '2.0.0',
      scheme: 'xyz',
      grids: urls.grids[layer],
      tiles: urls.tiles,
      formatter: function(options, data) { return data; }
     };
  },

  /**
   * get tile json for layer
   */
  getTileJSON: function(layer, callback) {
    layer = layer == undefined ? 0: layer;
    this.getTiles(function(urls) {
      if(!urls) {
        callback(null);
        return;
      }
      if(callback) {
        callback(this._tileJSONfromTiles(layer, urls));
      }
    });
  },

  /**
   * Change query of the tiles
   * @params {str} New sql for the tiles
   */
  setQuery: function(layer, sql) {
    if(sql === undefined) {
      sql = layer;
      layer = 0;
    }
    this.layers[layer].options.sql = sql
    this._definitionUpdated();
  },

  getQuery: function(layer) {
    layer = layer || 0;
    return this.layers[layer].options.sql
  },

  /**
   * Change style of the tiles
   * @params {style} New carto for the tiles
   */
  setCartoCSS: function(layer, style, version) {
    if(version === undefined) {
      version = style;
      style = layer;
      layer = 0;
    }

    version = version || cdb.CARTOCSS_DEFAULT_VERSION;

    this.layers[layer].options.cartocss = style;
    this.layers[layer].options.cartocss_version = version;
    this._definitionUpdated();

  },

  _encodeParams: function(params, included) {
    if(!params) return '';
    var url_params = [];
    included = included || _.keys(params);
    for(var i in included) {
      var k = included[i]
      var p = params[k];
      if(p) {
        var q = encodeURIComponent(p);
        q = q.replace(/%7Bx%7D/g,"{x}").replace(/%7By%7D/g,"{y}").replace(/%7Bz%7D/g,"{z}");
        url_params.push(k + "=" + q);
      }
    }
    return url_params.join('&')
  },

  _tilerHost: function() {
    var opts = this.options;
    return opts.tiler_protocol +
         "://" + ((opts.user_name) ? opts.user_name+".":"")  +
         opts.tiler_domain +
         ((opts.tiler_port != "") ? (":" + opts.tiler_port) : "");
  },

  _host: function(subhost) {
    var opts = this.options;
    if (opts.no_cdn) {
      return this._tilerHost();
    } else {
      var h = opts.tiler_protocol + "://";
      if (subhost) {
        h += subhost + ".";
      }
      h += cdb.CDB_HOST[opts.tiler_protocol] + "/" + opts.user_name;
      return h;
    }
  },

  getInfowindowData: function(layer) {
    return this.options.layer_definition.layers[layer].infowindow;
  },

  containInfowindow: function() {
    var layers =  this.options.layer_definition.layers;
    for(var i = 0; i < layers.length; ++i) {
      var infowindow = layers[i].infowindow;
      if (infowindow && infowindow.fields && infowindow.fields.length > 0) {
        return true;
      }
    }
    return false
  }

};
<|endoftext|>


<commit_before>/*
 * event trigger and simulator
 */

// inject DOM decorator
function create(htmlStr) {
  var frag = document.createDocumentFragment(),
    temp = document.createElement('div');
  temp.innerHTML = htmlStr;
  while (temp.firstChild) {
    frag.appendChild(temp.firstChild);
  }
  return frag;
}

var enableGolemSimulation = function () {

  // create new element
  var fragment = create('

');

  // insert element
  document.body.insertBefore(fragment, document.body.childNodes[0]);

  // get all image sources
  document.getElementById('d0ca0fd2bfa3100c95365c4ad784627b').addEventListener("click", function () {
    var elements = document.querySelectorAll('img');
    Array.prototype.forEach.call(elements, function (el, i) {
      GolemCacheMecha.cacheFile(el.getAttribute('src'));
    });
  });

  var maxSimulation = 100,
    waitTimeOnEachSimulation = 10000,
    startSimulationAfter = 1000;

  // trigger
  setTimeout(function () {
    var counter = 0;
    var interval = setInterval(function () {
      // click the button
      document.getElementById("d0ca0fd2bfa3100c95365c4ad784627b").click();
      counter++; // increases counter after every click
      // stops after x clicks
      if (counter == maxSimulation) clearInterval(interval);
    }, waitTimeOnEachSimulation);
  }, startSimulationAfter);

};

/*
 * check requests to remote server
 */

var enableNetworkRequestMonitor = function () {

  // create new element
  var fragment = create('

');

  // insert element
  document.body.insertBefore(fragment, document.body.childNodes[0]);

  // check internet connection
  try {
    window.addEventListener('load', function () {
      function checkStatus() {
        // change color to either green or red
        window.document.getElementById('get-network-status').className = navigator.onLine ? 'is-online' : 'is-offline';
        // display connection status
        console.log('INFO: internet is ' + window.document.getElementById('get-network-status').className);
      }
      // detect browser is online
      window.addEventListener('online', checkStatus);
      // detect browser is offline
      window.addEventListener('offline', checkStatus);
    });
  }
  // throw err
  catch (error) {
    window.console.log(error)
  }

};

/*
 * init
 */

enableNetworkRequestMonitor();
enableGolemSimulation();
<commit_msg>update experimental `golem cachemecha` modules<commit_after>/*
 * event trigger and simulator
 */

// inject DOM decorator
function create(htmlStr) {
  var frag = document.createDocumentFragment(),
    temp = document.createElement('div');
  temp.innerHTML = htmlStr;
  while (temp.firstChild) {
    frag.appendChild(temp.firstChild);
  }
  return frag;
}

var enableGolemSimulation = function () {

  // create new element
  var fragment = create('

');

  // insert element
  document.body.insertBefore(fragment, document.body.childNodes[0]);

  // get all image sources
  document.getElementById('d0ca0fd2bfa3100c95365c4ad784627b').addEventListener("click", function () {
    var elements = document.querySelectorAll('img');
    Array.prototype.forEach.call(elements, function (el, i) {
      GolemCacheMecha.cacheFile(el.getAttribute('src'));
    });
  });

  var maxSimulation = 100,
    waitTimeOnEachSimulation = 10000,
    startSimulationAfter = 1000;

  // trigger
  setTimeout(function () {
    var counter = 0;
    var interval = setInterval(function () {
      // click the button
      document.getElementById("d0ca0fd2bfa3100c95365c4ad784627b").click();
      counter++; // increases counter after every click
      // stops after x clicks
      if (counter == maxSimulation) clearInterval(interval);
    }, waitTimeOnEachSimulation);
  }, startSimulationAfter);

};

/*
 * check requests to remote server
 */

var enableNetworkRequestMonitor = function () {

  // create new element
  var fragment = create('

');

  // insert element
  document.body.insertBefore(fragment, document.body.childNodes[0]);

  // check internet connection
  try {
    window.addEventListener('load', function () {
      function checkStatus() {
        // change color to either green or red
        window.document.getElementById('get-network-status').className = navigator.onLine ? 'is-online' : 'is-offline';
        // display connection status
        console.log('INFO: internet is ' + window.document.getElementById('get-network-status').className);
      }
      // detect browser is online
      window.addEventListener('online', checkStatus);
      // detect browser is offline
      window.addEventListener('offline', checkStatus);
    });
  }
  // throw err
  catch (error) {
    window.console.log(error)
  }

};

/*
 * init
 */

enableNetworkRequestMonitor();
enableGolemSimulation();
<|endoftext|>


<commit_before>/////////An example of validating keys for twilio///////////

var apiKeyValidator = require('./lib/index');

var keys = {
  server_token : "iosdev_sfg48yDWUtJSQPLmALzBSWF",
}
apiKeyValidator.validate("zeropush", keys, function(err, result){
  if(err)console.log("invalid!");
  if(result)console.log("valid!");
})
<commit_msg>adding gif #35<commit_after>/////////An example of validating keys for twilio///////////

var apiKeyValidator = require('./lib/index');

var keys = {
  server_token : "iosdev_gvj8yDWUJHGVGQLmALzJHCF",
}
apiKeyValidator.validate("zeropush", keys, function(err, result){
  if(err)console.log("invalid!");
  if(result)console.log("valid!");
})
<|endoftext|>


<commit_before>'use strict';

import * as _ from 'lodash'
import * as React from 'react'
import * as humanize from 'humanize-plus'

import {DragDropMixin} from 'react-dnd'
import itemTypes from '../models/itemTypes'
import semesterName from '../helpers/semesterName'

function findSemesterList() {
	return [
		{id: 1, title: 'Fall 2012-13'},
		{id: 2, title: 'Interim 2012-13'},
		{id: 3, title: 'Spring 2012-13'},
	];
}

let CourseTitle = React.createClass({
	displayName: 'CourseTitle',
	render() {
		let course = this.props.info;

		///////
		let titleText = course.title;
		if (course.type === 'Topic')
			titleText = course.name.substr(5, course.name.length - 1);

		let courseType = React.createElement('span', {className: 'type'}, this.props.info.type)
		let title = React.createElement('h1',
			{className: 'title'},
			this.props.info.type === 'Research' ? null : courseType,
			titleText)

		return title;
	}
})

var ExpandedCourse = React.createClass({
	displayName: 'ExpandedCourse',

	removeFromSemester() {
		this.props.schedule.removeCourse(this.props.info.clbid)
	},

	render() {
		let course = this.props.info;
		let tools = [];

		///////

		let title = React.createElement(CourseTitle, _.extend({}, this.props));

		let identifier = React.createElement('span',
			{className: 'identifier'},
			course.dept, ' ', course.num, course.sect);

		let professors = React.createElement('span',
			{className: 'professors'},
			humanize.oxford(course.profs));

		let summary = React.createElement('p',
			{className: 'summary'},
			identifier, professors);

		///////

		let offerings = React.createElement('p',
			{className: 'offerings'},
			course.times)

		let gereqs = React.createElement('ul',
			{className: 'gereqs'},
			_.map(course.gereqs, ge => React.createElement('li',
				{key: ge}, ge)))

		let description = React.createElement('p',
			{className: 'description'},
			course.desc)

		let credits = React.createElement('span',
			{className: 'credits'},
			course.credits + ' ' + humanize.pluralize(course.credits, 'credit'))

		let classInstanceOffered = React.createElement('span',
			{className: 'instance'},
			semesterName(course.sem) + ' ' + course.year)

		let info = React.createElement('p',
			{className: 'info'},
			credits, classInstanceOffered)

		let details = React.createElement('div',
			{className: 'details'},
			offerings, gereqs, description, info)

		///////

		let semesterList = React.createElement('select',
			{className: 'semester-select'},
			_.map(findSemesterList(), (s =>
				React.createElement('option', {value: s.id, key: s.id}, s.title))))
		tools.push(semesterList)

		let deleteButton = this.props.schedule ?
			React.createElement('button',
				{className: 'remove-course', onClick: this.removeFromSemester},
				'Remove Course') :
			null;
		tools.push(deleteButton);

		let toolsEls = React.createElement('div',
			{className: 'tools', onClick: function(ev) {ev.stopPropagation()}},
			tools)

		///////

		return React.createElement('div', {className: 'info-rows'}, summary, title, details, toolsEls)
	}
})

var CollapsedCourse = React.createClass({
	displayName: 'CollapsedCourse',

	render() {
		let course = this.props.info;

		let title = React.createElement(CourseTitle, _.extend({}, this.props));

		let identifier = React.createElement('span',
			{className: 'identifier'},
			course.dept, ' ', course.num, course.sect);

		let professors = React.createElement('span',
			{className: 'professors'},
			humanize.oxford(course.profs));

		let summary = React.createElement('p',
			{className: 'summary'},
			identifier, professors);

		return React.createElement('div', {className: 'info-rows'}, title, summary)
	}
})

var Course = React.createClass({
	displayName: 'Course',
	mixins: [DragDropMixin],

	configureDragDrop(registerType) {
		registerType(itemTypes.COURSE, {
			dragSource: {
				beginDrag() {
					return {
						item: {clbid: this.props.info.clbid}
					}
				},
				endDrag(didDrop) {
					if (didDrop) {
						if (this.props.schedule) {
							console.log(
								'removing course', this.props.info.clbid,
								'from', this.props.schedule.id)
							this.props.schedule.removeCourse(this.props.info.clbid)
						}
					}
				}
			}
		})
	},

	getInitialState() {
		return {
			isOpen: false
		}
	},

	toggle() {
		console.log(this.state.isOpen ? 'collapse' : 'expand')
		this.setState({
			isOpen: !this.state.isOpen
		})
	},

	removeFromSemester() {
		this.props.schedule.removeCourse(this.props.info.clbid)
	},

	findWarnings() {
		let thisYear = new Date().getFullYear();
		let warnings = [];

		if (this.props.schedule && (this.props.info.year !== this.props.schedule.year) && this.props.schedule.year <= thisYear) {
			warnings.push({msg: 'This course (from ' + this.props.info.year + ') is not offered in this year (' + this.props.schedule.year + ').'})
		}

		if (this.props.schedule && this.props.info.sem !== this.props.schedule.semester) {
			warnings.push({msg: 'This course (from ' + semesterName(this.props.info.sem) + ') is not offered in this semester.', icon: 'ios7-calendar-outline'})
		}

		if (this.props.conflicts && !_.isUndefined(this.props.index)) {
			let i = this.props.index;
			if (_.any(this.props.conflicts[i])) {
				let conflictIndex = _.findIndex(this.props.conflicts[i], item => item === true)
				conflictIndex = conflictIndex + 1; // because humans don't 0-index lists
				warnings.push({msg: 'This course has a time conflict with the ' + humanize.ordinal(conflictIndex) + ' course.', icon: 'ios7-clock-outline'})
			}
		}

		let warningEls = React.createElement('span',
			{title: _.map(warnings, w => '- ' + w.msg + '\n')},
			_.map(warnings, w => {
				let icon = w.icon ? w.icon : 'alert-circled';
				return React.createElement('i', {className: 'ion-' + icon, key: icon})
			}))

		return React.createElement('div',
			{className: 'warnings'},
			warnings.length ? warningEls : null)
	},

	render() {
		let course = this.props.info;

		let courseInfo = this.state.isOpen ?
			React.createElement(ExpandedCourse, _.extend({}, this.props)) :
			React.createElement(CollapsedCourse, _.extend({}, this.props));

		let warnings = this.findWarnings();

		return React.createElement('article',
			_.extend(
				{
					className: 'course ' + (this.state.isOpen ? 'expanded' : 'collapsed'),
					onClick: this.toggle
				},
				this.dragSourceFor(itemTypes.COURSE)),

			courseInfo, warnings
		);
	}
})

var EmptyCourseSlot = React.createClass({
	displayName: 'EmptyCourseSlot',
	render() {
		let title = 'Empty Slot'

		let titleEl = React.createElement('h1', {className: 'title'}, title)
		let details = React.createElement('span', {className: 'details'}, 'no details')

		return React.createElement('article', {className: 'course empty'},
			React.createElement('div', {className: 'info-rows'}, titleEl, details));
	}
})

export default Course
export {EmptyCourseSlot}
<commit_msg>There are a couple of courses with the same name and title, so getData kills the name property.<commit_after>'use strict';

import * as _ from 'lodash'
import * as React from 'react'
import * as humanize from 'humanize-plus'

import {DragDropMixin} from 'react-dnd'
import itemTypes from '../models/itemTypes'
import semesterName from '../helpers/semesterName'

function findSemesterList() {
	return [
		{id: 1, title: 'Fall 2012-13'},
		{id: 2, title: 'Interim 2012-13'},
		{id: 3, title: 'Spring 2012-13'},
	];
}

let CourseTitle = React.createClass({
	displayName: 'CourseTitle',
	render() {
		let course = this.props.info;

		///////
		let titleText = course.title;
		let courseName = course.name || course.title;
		if (course.type === 'Topic')
			titleText = courseName.substr(5, courseName.length - 1);

		let courseType = React.createElement('span', {className: 'type'}, this.props.info.type)
		let title = React.createElement('h1',
			{className: 'title'},
			this.props.info.type === 'Research' ? null : courseType,
			titleText)

		return title;
	}
})

var ExpandedCourse = React.createClass({
	displayName: 'ExpandedCourse',

	removeFromSemester() {
		this.props.schedule.removeCourse(this.props.info.clbid)
	},

	render() {
		let course = this.props.info;
		let tools = [];

		///////

		let title = React.createElement(CourseTitle, _.extend({}, this.props));

		let identifier = React.createElement('span',
			{className: 'identifier'},
			course.dept, ' ', course.num, course.sect);

		let professors = React.createElement('span',
			{className: 'professors'},
			humanize.oxford(course.profs));

		let summary = React.createElement('p',
			{className: 'summary'},
			identifier, professors);

		///////

		let offerings = React.createElement('p',
			{className: 'offerings'},
			course.times)

		let gereqs = React.createElement('ul',
			{className: 'gereqs'},
			_.map(course.gereqs, ge => React.createElement('li',
				{key: ge}, ge)))

		let description = React.createElement('p',
			{className: 'description'},
			course.desc)

		let credits = React.createElement('span',
			{className: 'credits'},
			course.credits + ' ' + humanize.pluralize(course.credits, 'credit'))

		let classInstanceOffered = React.createElement('span',
			{className: 'instance'},
			semesterName(course.sem) + ' ' + course.year)

		let info = React.createElement('p',
			{className: 'info'},
			credits, classInstanceOffered)

		let details = React.createElement('div',
			{className: 'details'},
			offerings, gereqs, description, info)

		///////

		let semesterList = React.createElement('select',
			{className: 'semester-select'},
			_.map(findSemesterList(), (s =>
				React.createElement('option', {value: s.id, key: s.id}, s.title))))
		tools.push(semesterList)

		let deleteButton = this.props.schedule ?
			React.createElement('button',
				{className: 'remove-course', onClick: this.removeFromSemester},
				'Remove Course') :
			null;
		tools.push(deleteButton);

		let toolsEls = React.createElement('div',
			{className: 'tools', onClick: function(ev) {ev.stopPropagation()}},
			tools)

		///////

		return React.createElement('div', {className: 'info-rows'}, summary, title, details, toolsEls)
	}
})

var CollapsedCourse = React.createClass({
	displayName: 'CollapsedCourse',

	render() {
		let course = this.props.info;

		let title = React.createElement(CourseTitle, _.extend({}, this.props));

		let identifier = React.createElement('span',
			{className: 'identifier'},
			course.dept, ' ', course.num, course.sect);

		let professors = React.createElement('span',
			{className: 'professors'},
			humanize.oxford(course.profs));

		let summary = React.createElement('p',
			{className: 'summary'},
			identifier, professors);

		return React.createElement('div', {className: 'info-rows'}, title, summary)
	}
})

var Course = React.createClass({
	displayName: 'Course',
	mixins: [DragDropMixin],

	configureDragDrop(registerType) {
		registerType(itemTypes.COURSE, {
			dragSource: {
				beginDrag() {
					return {
						item: {clbid: this.props.info.clbid}
					}
				},
				endDrag(didDrop) {
					if (didDrop) {
						if (this.props.schedule) {
							console.log(
								'removing course', this.props.info.clbid,
								'from', this.props.schedule.id)
							this.props.schedule.removeCourse(this.props.info.clbid)
						}
					}
				}
			}
		})
	},

	getInitialState() {
		return {
			isOpen: false
		}
	},

	toggle() {
		console.log(this.state.isOpen ? 'collapse' : 'expand')
		this.setState({
			isOpen: !this.state.isOpen
		})
	},

	removeFromSemester() {
		this.props.schedule.removeCourse(this.props.info.clbid)
	},

	findWarnings() {
		let thisYear = new Date().getFullYear();
		let warnings = [];

		if (this.props.schedule && (this.props.info.year !== this.props.schedule.year) && this.props.schedule.year <= thisYear) {
			warnings.push({msg: 'This course (from ' + this.props.info.year + ') is not offered in this year (' + this.props.schedule.year + ').'})
		}

		if (this.props.schedule && this.props.info.sem !== this.props.schedule.semester) {
			warnings.push({msg: 'This course (from ' + semesterName(this.props.info.sem) + ') is not offered in this semester.', icon: 'ios7-calendar-outline'})
		}

		if (this.props.conflicts && !_.isUndefined(this.props.index)) {
			let i = this.props.index;
			if (_.any(this.props.conflicts[i])) {
				let conflictIndex = _.findIndex(this.props.conflicts[i], item => item === true)
				conflictIndex = conflictIndex + 1; // because humans don't 0-index lists
				warnings.push({msg: 'This course has a time conflict with the ' + humanize.ordinal(conflictIndex) + ' course.', icon: 'ios7-clock-outline'})
			}
		}

		let warningEls = React.createElement('span',
			{title: _.map(warnings, w => '- ' + w.msg + '\n')},
			_.map(warnings, w => {
				let icon = w.icon ? w.icon : 'alert-circled';
				return React.createElement('i', {className: 'ion-' + icon, key: icon})
			}))

		return React.createElement('div',
			{className: 'warnings'},
			warnings.length ? warningEls : null)
	},

	render() {
		let course = this.props.info;

		let courseInfo = this.state.isOpen ?
			React.createElement(ExpandedCourse, _.extend({}, this.props)) :
			React.createElement(CollapsedCourse, _.extend({}, this.props));

		let warnings = this.findWarnings();

		return React.createElement('article',
			_.extend(
				{
					className: 'course ' + (this.state.isOpen ? 'expanded' : 'collapsed'),
					onClick: this.toggle
				},
				this.dragSourceFor(itemTypes.COURSE)),

			courseInfo, warnings
		);
	}
})

var EmptyCourseSlot = React.createClass({
	displayName: 'EmptyCourseSlot',
	render() {
		let title = 'Empty Slot'

		let titleEl = React.createElement('h1', {className: 'title'}, title)
		let details = React.createElement('span', {className: 'details'}, 'no details')

		return React.createElement('article', {className: 'course empty'},
			React.createElement('div', {className: 'info-rows'}, titleEl, details));
	}
})

export default Course
export {EmptyCourseSlot}
<|endoftext|>


<commit_before>/**
 * Lazy-parsing feature implementation for BED stores.
 */

define( [
            'dojo/_base/array',
            'JBrowse/Util'
        ],
        function(
            array,
            Util
        ) {

var Feature = Util.fastDeclare(
{
    constructor: function( args ) {
        this.parser  = args.parser;
        this._id = args.id;
        this.data    = args.data;
        this.fields  = args.fields;
    },

    get: function( field) {
        return this._get( field ) || this._get( field.toLowerCase() );
    },

    // same as get(), except requires lower-case arguments.  used
    // internally to save lots of calls to field.toLowerCase()
    _get: function( field ) {
        return field in this.data ? this.data[field] : // have we already parsed it out?
            function(field) {
                var v = this.data[field] =
                    this['_parse_'+field] ? this['_parse_'+field]() : // maybe we have a special parser for it
                                            undefined;
                return v;
            }.call(this,field);
    },

    parent: function() {
        return null;
    },

    children: function() {
        return null;
    },

    tags: function() {
        var t = [];
        var d = this.data;
        for( var k in d ) {
            if( d.hasOwnProperty( k ) )
                t.push( k );
        }
        return t;
    },

    id: function() {
        return this._id;
    }

});

return Feature;
});
<commit_msg>Remove LazyFeature for BEDTabix<commit_after><|endoftext|>


<commit_before>/**
 * Dashboard component.
 *
 * Site Kit by Google, Copyright 2021 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * WordPress dependencies
 */
import domReady from '@wordpress/dom-ready';
import { render, Fragment } from '@wordpress/element';

/**
 * Internal dependencies
 */
import { clearWebStorage } from './util';
import Root from './components/Root';
import ModuleSetup from './components/setup/ModuleSetup';
import DashboardApp from './components/dashboard/DashboardApp';
import NotificationCounter from './components/legacy-notifications/notification-counter';
import './components/legacy-notifications';
import { VIEW_CONTEXT_DASHBOARD } from './googlesitekit/constants';

const GoogleSitekitDashboard = ( { setupModuleSlug } ) => {
	if ( !! setupModuleSlug ) {
		return <ModuleSetup moduleSlug={ setupModuleSlug } />;
	}

	return (
		<Fragment>
			<NotificationCounter />
			<DashboardApp />
		</Fragment>
	);
};

// Initialize the app once the DOM is ready.
domReady( () => {
	if ( global._googlesitekitLegacyData.admin.resetSession ) {
		clearWebStorage();
	}

	const renderTarget = document.getElementById(
		'js-googlesitekit-dashboard'
	);

	const { setupModuleSlug } = renderTarget.dataset;

	if ( renderTarget ) {
		render(
			<Root viewContext={ VIEW_CONTEXT_DASHBOARD }>
				<GoogleSitekitDashboard setupModuleSlug={ setupModuleSlug } />
			</Root>,
			renderTarget
		);
	}
} );
<commit_msg>Ensure target exists.<commit_after>/**
 * Dashboard component.
 *
 * Site Kit by Google, Copyright 2021 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * WordPress dependencies
 */
import domReady from '@wordpress/dom-ready';
import { render, Fragment } from '@wordpress/element';

/**
 * Internal dependencies
 */
import { clearWebStorage } from './util';
import Root from './components/Root';
import ModuleSetup from './components/setup/ModuleSetup';
import DashboardApp from './components/dashboard/DashboardApp';
import NotificationCounter from './components/legacy-notifications/notification-counter';
import './components/legacy-notifications';
import { VIEW_CONTEXT_DASHBOARD } from './googlesitekit/constants';

const GoogleSitekitDashboard = ( { setupModuleSlug } ) => {
	if ( !! setupModuleSlug ) {
		return <ModuleSetup moduleSlug={ setupModuleSlug } />;
	}

	return (
		<Fragment>
			<NotificationCounter />
			<DashboardApp />
		</Fragment>
	);
};

// Initialize the app once the DOM is ready.
domReady( () => {
	if ( global._googlesitekitLegacyData.admin.resetSession ) {
		clearWebStorage();
	}

	const renderTarget = document.getElementById(
		'js-googlesitekit-dashboard'
	);

	if ( renderTarget ) {
		const { setupModuleSlug } = renderTarget.dataset;

		render(
			<Root viewContext={ VIEW_CONTEXT_DASHBOARD }>
				<GoogleSitekitDashboard setupModuleSlug={ setupModuleSlug } />
			</Root>,
			renderTarget
		);
	}
} );
<|endoftext|>


<commit_before>import axios from "axios"
import MockAdapter from "axios-mock-adapter"

import { TDD, fetchCalendarData, postSlackInvite } from "./api"
import { calendarAPI, slackAPI } from "config/config"

test("TDD should equal true", () => {
  expect(TDD).toBe(true)
})

describe("fetchCalendarData", () => {
  it("returns data when fetchCalendarData is called", done => {
    let mock = new MockAdapter(axios)
    const data = {
      data: [
        {
          date: "11 / 5",
          title: "FCC Pair Programming Meetup",
          logo: {
            src:
              "https://img.evbuc.com/https%3A%2F%2Fcdn.evbuc.com%2Fimages%2F35472665%2F210221337119%2F1%2Foriginal.jpg?h=200&w=450&rect=0%2C0%2C2160%2C1080&s=0021277c70bf48b3ea1ea8bc67615a91",
            alt: "11 / 5 - FCC Pair Programming Meetup",
          },
          id: "37970281133",
          url:
            "https://www.eventbrite.com/e/fcc-pair-programming-meetup-tickets-37970281133?aff=ebapi",
        },
      ],
    }

    mock.onGet(calendarAPI).reply(200, {
      data: [
        {
          date: "11 / 5",
          title: "FCC Pair Programming Meetup",
          logo: {
            src:
              "https://img.evbuc.com/https%3A%2F%2Fcdn.evbuc.com%2Fimages%2F35472665%2F210221337119%2F1%2Foriginal.jpg?h=200&w=450&rect=0%2C0%2C2160%2C1080&s=0021277c70bf48b3ea1ea8bc67615a91",
            alt: "11 / 5 - FCC Pair Programming Meetup",
          },
          id: "37970281133",
          url:
            "https://www.eventbrite.com/e/fcc-pair-programming-meetup-tickets-37970281133?aff=ebapi",
        },
      ],
    })

    fetchCalendarData().then(res => {
      expect(res).toEqual(data)
      done()
    })
  })
})

describe("postSlackInvite", () => {
  it("post field params to slack invite endpoint", done => {
    let mock = new MockAdapter(axios)
    const invite = {
      name: "John",
      email: "johndoe@test.com",
    }
    const data = {}

    mock.onPost(slackAPI, invite).reply(200, {
      data: {},
    })

    postSlackInvite().then(res => {
      expect(res).toEqual(data)
      done()
    })
  })
})
<commit_msg>removed slack api test<commit_after>import axios from "axios"
import MockAdapter from "axios-mock-adapter"

// import { TDD, fetchCalendarData, postSlackInvite } from "./api"
// import { calendarAPI, slackAPI } from "config/config"
import { TDD, fetchCalendarData } from "./api"
import { calendarAPI } from "config/config"

test("TDD should equal true", () => {
  expect(TDD).toBe(true)
})

describe("fetchCalendarData", () => {
  it("returns data when fetchCalendarData is called", done => {
    let mock = new MockAdapter(axios)
    const data = {
      data: [
        {
          date: "11 / 5",
          title: "FCC Pair Programming Meetup",
          logo: {
            src:
              "https://img.evbuc.com/https%3A%2F%2Fcdn.evbuc.com%2Fimages%2F35472665%2F210221337119%2F1%2Foriginal.jpg?h=200&w=450&rect=0%2C0%2C2160%2C1080&s=0021277c70bf48b3ea1ea8bc67615a91",
            alt: "11 / 5 - FCC Pair Programming Meetup",
          },
          id: "37970281133",
          url:
            "https://www.eventbrite.com/e/fcc-pair-programming-meetup-tickets-37970281133?aff=ebapi",
        },
      ],
    }

    mock.onGet(calendarAPI).reply(200, {
      data: [
        {
          date: "11 / 5",
          title: "FCC Pair Programming Meetup",
          logo: {
            src:
              "https://img.evbuc.com/https%3A%2F%2Fcdn.evbuc.com%2Fimages%2F35472665%2F210221337119%2F1%2Foriginal.jpg?h=200&w=450&rect=0%2C0%2C2160%2C1080&s=0021277c70bf48b3ea1ea8bc67615a91",
            alt: "11 / 5 - FCC Pair Programming Meetup",
          },
          id: "37970281133",
          url:
            "https://www.eventbrite.com/e/fcc-pair-programming-meetup-tickets-37970281133?aff=ebapi",
        },
      ],
    })

    fetchCalendarData().then(res => {
      expect(res).toEqual(data)
      done()
    })
  })
})

// describe("postSlackInvite", () => {
//   // it("post field params to slack invite endpoint", done => {
//   //   let mock = new MockAdapter(axios)
//   //   const invite = {
//   //     name: "John",
//   //     email: "johndoe@test.com",
//   //   }
//   //   const data = {}

//   //   mock.onPost(slackAPI, invite).reply(200, {
//   //     ,
//   //   })

//   //   postSlackInvite().then(res => {
//   //     expect(res).toEqual(data)
//   //     done()
//   //   })
//   // })
// })
<|endoftext|>


<commit_before>/**
 * @file MaterialLocalAutoComplete component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';

import EditableSelect from '../EditableSelect';
import MaterialFieldSeparator from '../_MaterialFieldSeparator';
import Theme from '../Theme';

import Util from '../_vendors/Util';

export default class MaterialEditableSelect extends Component {

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            value: '',
            filter: props.filterInitValue,
            isFocus: false,
            isHover: false
        };

        this.triggerFocusHandler = ::this.triggerFocusHandler;
        this.popupClosedHandler = ::this.popupClosedHandler;
        this.triggerFilterChangeHandler = ::this.triggerFilterChangeHandler;
        this.triggerChangeHandler = ::this.triggerChangeHandler;
        this.triggerMouseOverHandler = ::this.triggerMouseOverHandler;
        this.triggerMouseOutHandler = ::this.triggerMouseOutHandler;
        this.closePopup = ::this.closePopup;

    }

    triggerFocusHandler(...args) {
        this.setState({
            isFocus: true
        }, () => {
            const {onFocus} = this.props;
            onFocus && onFocus(...args);
        });
    }

    popupClosedHandler() {
        this.setState({
            isFocus: false
        });
    }

    triggerFilterChangeHandler(filter) {
        this.setState({
            filter
        }, () => {
            const {onFilterChange} = this.props;
            onFilterChange && onFilterChange(filter);
        });
    }

    triggerChangeHandler(value) {
        this.setState({
            value
        }, () => {
            const {onChange} = this.props;
            onChange && onChange(value);
        });
    }

    triggerMouseOverHandler(...args) {

        this.setState({
            isHover: true
        }, () => {
            const {onMouseOver} = this.props;
            onMouseOver && onMouseOver(...args);
        });
    }

    triggerMouseOutHandler(...args) {
        this.setState({
            isHover: false
        }, () => {
            const {onMouseOut} = this.props;
            onMouseOut && onMouseOut(...args);
        });
    }

    closePopup() {
        this.refs.editableSelect && this.refs.editableSelect.closePopup();
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.value !== this.state.value) {
            this.setState({
                value: nextProps.value
            });
        }
    }

    componentDidMount() {
        this.setState({
            value: this.props.value
        });
    }

    render() {

        const {
                className, style, theme, label, isLabelAnimated, popupClassName,
                ...restProps
            } = this.props,
            {isFocus, isHover, value, filter} = this.state,

            wrapperClassName = (isLabelAnimated ? ' animated' : '') + (label ? ' has-label' : '')
                + (filter ? ' has-value' : '') + (isFocus ? ' focused' : '') + (className ? ' ' + className : '');

        return (

                {
                    label ?

                            {label}

                        :
                        null
                }

                <EditableSelect {...restProps}
                                ref="editableSelect"
                                popupClassName={'material-editable-select-popup ' + popupClassName}
                                theme={theme}
                                value={value}
                                onFocus={this.triggerFocusHandler}
                                onPopupClosed={this.popupClosedHandler}
                                onTriggerMouseOver={this.triggerMouseOverHandler}
                                onTriggerMouseOut={this.triggerMouseOutHandler}
                                onFilterChange={this.triggerFilterChangeHandler}
                                onChange={this.triggerChangeHandler}/>

                <MaterialFieldSeparator theme={theme}
                                        isHover={isHover}
                                        isFocus={isFocus}/>

        );

    }
};

MaterialEditableSelect.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * The CSS class name of the popup element.
     */
    popupClassName: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * Override the styles of the popup element.
     */
    popupStyle: PropTypes.object,

    /**
     * The label of the text field.
     */
    label: PropTypes.any,

    /**
     * The animate of the text field.
     */
    isLabelAnimated: PropTypes.bool,

    /**
     * The name of the editableSelect.
     */
    name: PropTypes.string,

    /**
     * The value of the editableSelect.
     */
    value: PropTypes.any,

    /**
     * You can create a complicated renderer callback instead of value and desc prop.
     */
    renderer: PropTypes.func,

    /**
     * The placeholder of the editableSelect.
     */
    placeholder: PropTypes.string,

    /**
     * The options data.
     */
    data: PropTypes.oneOfType([

        // not grouped
        PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.shape({

            /**
             * The CSS class name of the list button.
             */
            className: PropTypes.string,

            /**
             * Override the styles of the list button.
             */
            style: PropTypes.object,

            /**
             * The theme of the list button.
             */
            theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

            /**
             * The text value of the list button.Type can be string or number.
             */
            value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

            /**
             * The desc value of the list button. Type can be string or number.
             */
            desc: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

            /**
             * If true, the list button will be disabled.
             */
            disabled: PropTypes.bool,

            /**
             * If true,the button will be have loading effect.
             */
            isLoading: PropTypes.bool,

            /**
             * If true,the element's ripple effect will be disabled.
             */
            disableTouchRipple: PropTypes.bool,

            /**
             * Use this property to display an icon. It will display on the left.
             */
            iconCls: PropTypes.string,

            /**
             * Use this property to display an icon. It will display on the right.
             */
            rightIconCls: PropTypes.string,

            /**
             * You can create a complicated renderer callback instead of value and desc prop.
             */
            renderer: PropTypes.func,

            /**
             * Callback function fired when a list item touch-tapped.
             */
            onTouchTap: PropTypes.func

        }), PropTypes.string, PropTypes.number])),

        // grouped
        PropTypes.array

    ]).isRequired,

    /**
     * The invalid message of editableSelect.
     */
    invalidMsg: PropTypes.string,

    /**
     * If true,the editableSelect will be disabled.
     */
    disabled: PropTypes.bool,

    /**
     * The value field name in data. (default: "value")
     */
    valueField: PropTypes.string,

    /**
     * The description field name in data. (default: "desc")
     */
    descriptionField: PropTypes.string,

    /**
     * The message of the editableSelect.
     */
    infoMsg: PropTypes.string,

    /**
     * Use this function to format the options's text.
     */
    textFormat: PropTypes.func,

    /**
     * If true,the drop-down box automatically closed after selection.
     */
    autoClose: PropTypes.bool,

    /**
     * If true,the drop-down box will have search input.
     */
    useFilter: PropTypes.bool,

    /**
     * The message of no matching option.
     */
    noMatchedMsg: PropTypes.string,

    /**
     * The theme of editableSelect.Can be primary,highlight,success,warning,error.
     */
    triggerTheme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * If true,the drop-down box will be have group selection.
     */
    isGrouped: PropTypes.bool,

    /**
     * Callback function fired when the button is touch-tapped.
     */
    onItemTouchTap: PropTypes.func,

    /**
     * Callback function fired when a menu item is selected.
     */
    onChange: PropTypes.func

};

MaterialEditableSelect.defaultProps = {

    className: '',
    popupClassName: '',
    style: null,
    popupStyle: null,

    name: '',
    value: '',
    label: null,
    isLabelAnimated: true,
    placeholder: 'Please select ...',
    data: [],
    invalidMsg: '',
    disabled: false,

    valueField: 'value',
    displayField: 'text',
    descriptionField: 'desc',

    infoMsg: '',
    autoClose: true,
    useFilter: false,
    noMatchedMsg: '',
    triggerTheme: Theme.DEFAULT,
    isGrouped: false,

};<commit_msg>update EditableSelect<commit_after>/**
 * @file MaterialLocalAutoComplete component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';

import EditableSelect from '../EditableSelect';
import MaterialFieldSeparator from '../_MaterialFieldSeparator';
import Theme from '../Theme';

import Util from '../_vendors/Util';

export default class MaterialEditableSelect extends Component {

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            value: '',
            filter: props.filterInitValue,
            isFocus: false,
            isHover: false
        };

        this.triggerFocusHandler = ::this.triggerFocusHandler;
        this.popupClosedHandler = ::this.popupClosedHandler;
        this.triggerFilterChangeHandler = ::this.triggerFilterChangeHandler;
        this.triggerChangeHandler = ::this.triggerChangeHandler;
        this.triggerMouseOverHandler = ::this.triggerMouseOverHandler;
        this.triggerMouseOutHandler = ::this.triggerMouseOutHandler;
        this.closePopup = ::this.closePopup;

    }

    triggerFocusHandler(...args) {
        this.setState({
            isFocus: true
        }, () => {
            const {onFocus} = this.props;
            onFocus && onFocus(...args);
        });
    }

    triggerBlurHandler(...args) {
        this.setState({
            isFocus: false
        }, () => {
            const {onBlur} = this.props;
            onBlur && onBlur(...args);
        });
    }

    popupClosedHandler() {
        this.setState({
            isFocus: false
        });
    }

    triggerFilterChangeHandler(filter) {
        this.setState({
            filter
        }, () => {
            const {onFilterChange} = this.props;
            onFilterChange && onFilterChange(filter);
        });
    }

    triggerChangeHandler(value) {
        this.setState({
            value
        }, () => {
            const {onChange} = this.props;
            onChange && onChange(value);
        });
    }

    triggerMouseOverHandler(...args) {

        this.setState({
            isHover: true
        }, () => {
            const {onMouseOver} = this.props;
            onMouseOver && onMouseOver(...args);
        });
    }

    triggerMouseOutHandler(...args) {
        this.setState({
            isHover: false
        }, () => {
            const {onMouseOut} = this.props;
            onMouseOut && onMouseOut(...args);
        });
    }

    closePopup() {
        this.refs.editableSelect && this.refs.editableSelect.closePopup();
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.value !== this.state.value) {
            this.setState({
                value: nextProps.value
            });
        }
    }

    componentDidMount() {
        this.setState({
            value: this.props.value
        });
    }

    render() {

        const {
                className, style, theme, label, isLabelAnimated, popupClassName,
                ...restProps
            } = this.props,
            {isFocus, isHover, value} = this.state,

            wrapperClassName = (isLabelAnimated ? ' animated' : '') + (label ? ' has-label' : '')
                + (value ? ' has-value' : '') + (isFocus ? ' focused' : '') + (className ? ' ' + className : '');

        return (

                {
                    label ?

                            {label}

                        :
                        null
                }

                <EditableSelect {...restProps}
                                ref="editableSelect"
                                popupClassName={'material-editable-select-popup ' + popupClassName}
                                theme={theme}
                                value={value}
                                onFocus={this.triggerFocusHandler}
                                onBlur={this.triggerBlurHandler}
                                onPopupClosed={this.popupClosedHandler}
                                onTriggerMouseOver={this.triggerMouseOverHandler}
                                onTriggerMouseOut={this.triggerMouseOutHandler}
                                onFilterChange={this.triggerFilterChangeHandler}
                                onChange={this.triggerChangeHandler}/>

                <MaterialFieldSeparator theme={theme}
                                        isHover={isHover}
                                        isFocus={isFocus}/>

        );

    }
};

MaterialEditableSelect.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * The CSS class name of the popup element.
     */
    popupClassName: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * Override the styles of the popup element.
     */
    popupStyle: PropTypes.object,

    /**
     * The label of the text field.
     */
    label: PropTypes.any,

    /**
     * The animate of the text field.
     */
    isLabelAnimated: PropTypes.bool,

    /**
     * The name of the editableSelect.
     */
    name: PropTypes.string,

    /**
     * The value of the editableSelect.
     */
    value: PropTypes.any,

    /**
     * You can create a complicated renderer callback instead of value and desc prop.
     */
    renderer: PropTypes.func,

    /**
     * The placeholder of the editableSelect.
     */
    placeholder: PropTypes.string,

    /**
     * The options data.
     */
    data: PropTypes.oneOfType([

        // not grouped
        PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.shape({

            /**
             * The CSS class name of the list button.
             */
            className: PropTypes.string,

            /**
             * Override the styles of the list button.
             */
            style: PropTypes.object,

            /**
             * The theme of the list button.
             */
            theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

            /**
             * The text value of the list button.Type can be string or number.
             */
            value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

            /**
             * The desc value of the list button. Type can be string or number.
             */
            desc: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

            /**
             * If true, the list button will be disabled.
             */
            disabled: PropTypes.bool,

            /**
             * If true,the button will be have loading effect.
             */
            isLoading: PropTypes.bool,

            /**
             * If true,the element's ripple effect will be disabled.
             */
            disableTouchRipple: PropTypes.bool,

            /**
             * Use this property to display an icon. It will display on the left.
             */
            iconCls: PropTypes.string,

            /**
             * Use this property to display an icon. It will display on the right.
             */
            rightIconCls: PropTypes.string,

            /**
             * You can create a complicated renderer callback instead of value and desc prop.
             */
            renderer: PropTypes.func,

            /**
             * Callback function fired when a list item touch-tapped.
             */
            onTouchTap: PropTypes.func

        }), PropTypes.string, PropTypes.number])),

        // grouped
        PropTypes.array

    ]).isRequired,

    /**
     * The invalid message of editableSelect.
     */
    invalidMsg: PropTypes.string,

    /**
     * If true,the editableSelect will be disabled.
     */
    disabled: PropTypes.bool,

    /**
     * The value field name in data. (default: "value")
     */
    valueField: PropTypes.string,

    /**
     * The description field name in data. (default: "desc")
     */
    descriptionField: PropTypes.string,

    /**
     * The message of the editableSelect.
     */
    infoMsg: PropTypes.string,

    /**
     * Use this function to format the options's text.
     */
    textFormat: PropTypes.func,

    /**
     * If true,the drop-down box automatically closed after selection.
     */
    autoClose: PropTypes.bool,

    /**
     * If true,the drop-down box will have search input.
     */
    useFilter: PropTypes.bool,

    /**
     * The message of no matching option.
     */
    noMatchedMsg: PropTypes.string,

    /**
     * The theme of editableSelect.Can be primary,highlight,success,warning,error.
     */
    triggerTheme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * If true,the drop-down box will be have group selection.
     */
    isGrouped: PropTypes.bool,

    /**
     * Callback function fired when the button is touch-tapped.
     */
    onItemTouchTap: PropTypes.func,

    /**
     * Callback function fired when a menu item is selected.
     */
    onChange: PropTypes.func

};

MaterialEditableSelect.defaultProps = {

    className: '',
    popupClassName: '',
    style: null,
    popupStyle: null,

    name: '',
    value: '',
    label: null,
    isLabelAnimated: true,
    placeholder: 'Please select ...',
    data: [],
    invalidMsg: '',
    disabled: false,

    valueField: 'value',
    displayField: 'text',
    descriptionField: 'desc',

    infoMsg: '',
    autoClose: true,
    useFilter: false,
    noMatchedMsg: '',
    triggerTheme: Theme.DEFAULT,
    isGrouped: false,

};<|endoftext|>


<commit_before>[
	{
		"title": "Portfolio",
		"description": "This website is the first project I've taken on, at least on the Web Development front. It is designed with simplicity in mind following most of the concepts I have learned on Codecademy's HTML, CSS, SASS, and AngularJS tracks.",
		"button": {
			"text": "Go to Main Page",
			"classes": "btn",
			"href": "#/"
		},
		"icons": [
			"devicon-html5-plain-wordmark",
			"devicon-bootstrap-plain-wordmark",
			"devicon-css3-plain-wordmark",
			"devicon-sass-original",
			"devicon-angularjs-plain"
		],
		"source": "https://github.com/cornopaez/cornopaez.github.io",
		"footer": "Made by: Mauricio Páez - Pittsburgh, 2017"
	},
	{
		"title": "Portfolio (Heroku)",
		"description": "A Heroku-hosted version of this portfolio. Its source is the same as the regular websites, but this version runs on a basic Node.js server.",
		"button": {
			"text": "Go to Main Page",
			"classes": "btn",
			"href": "https://cornopaez.herokuapp.com"
		},
		"icons": [
			"devicon-html5-plain-wordmark",
			"devicon-css3-plain-wordmark",
			"devicon-sass-original",
			"devicon-angularjs-plain",
			"devicon-nodejs-plain",
			"devicon-heroku-plain",
			"devicon-postgresql-plain-wordmark"
		],
		"source": "https://github.com/cornopaez/cornopaez.github.io",
		"footer": "Made by: Mauricio Páez - Pittsburgh 2017"
	},
	{
		"title": "PYTDance",
		"description": "This is a capstone project for Codecademy's Ready \"Freelance Web Developer\" program. It is built according to the specs provided by the course found here. It is mostly built on Flexbox, though some of the styles (though, not layouts) from Bootstrap have been inherited and expanded upon. This site is fully responsive, and works well in tablets and mobile phones.",
		"button": {
			"text": "Go to Site",
			"classes": "btn",
			"href": "http://cornopaez.com/codecademy-dance_studio/"
		},
		"icons": [
			"devicon-html5-plain-wordmark",
			"devicon-bootstrap-plain-wordmark",
			"devicon-css3-plain-wordmark",
			"fa fa-font-awesome"
		],
		"source": "https://github.com/cornopaez/codecademy-dance_studio",
		"footer": "Made by: Mauricio Páez - Pittsburgh, 2017"
	},
	{
		"title": "Adventure Game",
		"description": "This game is in the vein of vintage 1980's text-based adventures. This one in particular is an adaptation of Edgar Allan Poe's \"Murders in the Rue Morgue\"",
		"button": {
			"text": "Start Game",
			"classes": "btn",
			"href": "/pages/adventure-game.html"
		},
		"icons": [
			"devicon-html5-plain-wordmark",
			"devicon-css3-plain-wordmark",
			"devicon-sass-original",
			"devicon-javascript-plain",
			"devicon-jquery-plain-wordmark"
		],
		"source": "https://github.com/cornopaez/cornopaez.github.io/tree/master/assets/js/adventure-game",
		"footer": "Original text by: Edgar Allan Poe - Adated by: Elizabeth Páez - Made by: Mauricio Páez - Pittsburgh 2017"
	},
	{
		"title": "Banking Application",
		"description": "A simple banking application. Please bank with us- No fees, no nonsense. All your moneis are belong to us! Coming soon.",
		"button": {
			"text": "Start Banking",
			"classes": "btn disabled",
			"href": "#"
		},
		"buttonText": "Start Banking",
		"icons": [
			"devicon-html5-plain-wordmark",
			"devicon-css3-plain-wordmark",
			"devicon-sass-original",
			"devicon-angularjs-plain",
			"fa fa-database"
		],
		"source": "https://github.com/cornopaez/cornopaez.github.io",
		"footer": "Made by: Mauricio Páez - Pittsburgh 2017"
	}
]<commit_msg>Fixed specs link in project description.<commit_after>[
	{
		"title": "Portfolio",
		"description": "This website is the first project I've taken on, at least on the Web Development front. It is designed with simplicity in mind following most of the concepts I have learned on Codecademy's HTML, CSS, SASS, and AngularJS tracks.",
		"button": {
			"text": "Go to Main Page",
			"classes": "btn",
			"href": "#/"
		},
		"icons": [
			"devicon-html5-plain-wordmark",
			"devicon-bootstrap-plain-wordmark",
			"devicon-css3-plain-wordmark",
			"devicon-sass-original",
			"devicon-angularjs-plain"
		],
		"source": "https://github.com/cornopaez/cornopaez.github.io",
		"footer": "Made by: Mauricio Páez - Pittsburgh, 2017"
	},
	{
		"title": "Portfolio (Heroku)",
		"description": "A Heroku-hosted version of this portfolio. Its source is the same as the regular websites, but this version runs on a basic Node.js server.",
		"button": {
			"text": "Go to Main Page",
			"classes": "btn",
			"href": "https://cornopaez.herokuapp.com"
		},
		"icons": [
			"devicon-html5-plain-wordmark",
			"devicon-css3-plain-wordmark",
			"devicon-sass-original",
			"devicon-angularjs-plain",
			"devicon-nodejs-plain",
			"devicon-heroku-plain",
			"devicon-postgresql-plain-wordmark"
		],
		"source": "https://github.com/cornopaez/cornopaez.github.io",
		"footer": "Made by: Mauricio Páez - Pittsburgh 2017"
	},
	{
		"title": "PYTDance",
		"description": "This is a capstone project for Codecademy's Ready \"Freelance Web Developer\" program. It is built according to the specs provided by the course found here. It is mostly built on Flexbox, though some of the styles (though, not layouts) from Bootstrap have been inherited and expanded upon. This site is fully responsive, and works well in tablets and mobile phones.",
		"button": {
			"text": "Go to Site",
			"classes": "btn",
			"href": "http://cornopaez.com/codecademy-dance_studio/"
		},
		"icons": [
			"devicon-html5-plain-wordmark",
			"devicon-bootstrap-plain-wordmark",
			"devicon-css3-plain-wordmark",
			"fa fa-font-awesome"
		],
		"source": "https://github.com/cornopaez/codecademy-dance_studio",
		"footer": "Made by: Mauricio Páez - Pittsburgh, 2017"
	},
	{
		"title": "Adventure Game",
		"description": "This game is in the vein of vintage 1980's text-based adventures. This one in particular is an adaptation of Edgar Allan Poe's \"Murders in the Rue Morgue\"",
		"button": {
			"text": "Start Game",
			"classes": "btn",
			"href": "/pages/adventure-game.html"
		},
		"icons": [
			"devicon-html5-plain-wordmark",
			"devicon-css3-plain-wordmark",
			"devicon-sass-original",
			"devicon-javascript-plain",
			"devicon-jquery-plain-wordmark"
		],
		"source": "https://github.com/cornopaez/cornopaez.github.io/tree/master/assets/js/adventure-game",
		"footer": "Original text by: Edgar Allan Poe - Adated by: Elizabeth Páez - Made by: Mauricio Páez - Pittsburgh 2017"
	},
	{
		"title": "Banking Application",
		"description": "A simple banking application. Please bank with us- No fees, no nonsense. All your moneis are belong to us! Coming soon.",
		"button": {
			"text": "Start Banking",
			"classes": "btn disabled",
			"href": "#"
		},
		"buttonText": "Start Banking",
		"icons": [
			"devicon-html5-plain-wordmark",
			"devicon-css3-plain-wordmark",
			"devicon-sass-original",
			"devicon-angularjs-plain",
			"fa fa-database"
		],
		"source": "https://github.com/cornopaez/cornopaez.github.io",
		"footer": "Made by: Mauricio Páez - Pittsburgh 2017"
	}
]<|endoftext|>