<commit_before>var log = require('cdb.log');
var _ = require('underscore');
var L = require('leaflet');
var TC = require('tangram.cartodb');
var LeafletLayerView = require('./leaflet-layer-view');
var Profiler = require('../../core/profiler');

var LeafletCartoDBWebglLayerGroupView = function (layerGroupModel, leafletMap, showLimitErrors) {
  var self = this;
  LeafletLayerView.apply(this, arguments);
  var metric = Profiler.metric('tangram.rendering');

  metric.start();

  this.trigger('loading');

  this.tangram = new TC(leafletMap, this.initConfig.bind(this, layerGroupModel), showLimitErrors);

  this.tangram.onLoaded(function () {
    if (metric) {
      self.trigger('load');
      metric.end();
      metric = void 0;

      log.info('Rendered Geometries Count: ', self.tangram.getTotalGeometries());
    }
  });

  this.layerGroupModel = layerGroupModel;
};

LeafletCartoDBWebglLayerGroupView.prototype = _.extend(
  {},
  LeafletLayerView.prototype,
  {
    initConfig: function (layerGroupModel) {
      var onURLsChanged = this._onURLsChanged(layerGroupModel);

      layerGroupModel.bind('change:urls', onURLsChanged);

      layerGroupModel.forEachGroupedLayer(this._onLayerAdded, this);
      layerGroupModel.onLayerAdded(this._onLayerAdded.bind(this));

      this._addInteractiveEvents();
      this._addErrorsEvents();
    },

    _addInteractiveEvents: function () {
      var hovered = false;
      var self = this;
      this.tangram.layer.setSelectionEvents({
        hover: function (e) {
          if (e.feature) {
            hovered = true;
            self.trigger('featureOver', self._getFeatureObject(e));
          } else if (hovered) {
            hovered = false;
            self.trigger('featureOut', self._getFeatureObject(e));
          }
        },
        click: function (e) {
          if (e.feature) {
            self.trigger('featureClick', self._getFeatureObject(e));
          }
        }
      });
    },

    // This errors/warnings will come from Tangram. Bad thing is tangram doesn't
    // include error stus code, only error text, so we need to make some parsing
    // in order to segment limits errors (Too many requests)
    _addErrorsEvents: function () {
      this.tangram.scene.subscribe({
        tileError: function (error) {
          var code = parseInt(error.statusCode);

          switch (code) {
            case 429:
              this.layerGroupModel.addError({ type: 'limit' });
              break;
            case 204:
              // This error is thrown when the tile has no data
              break;
            default:
              this.layerGroupModel.addError({ type: 'tile' });
          }
        }.bind(this)
      });
    },

    _getFeatureObject: function (e) {
      var layer = this.layerGroupModel.getCartoLayerById(e.feature && e.feature.source_layer);
      if (layer) {
        var index = this.layerGroupModel.getIndexOfLayerInLayerGroup(layer);
        this.lastLayer = layer;
        this.lastLayerIndex = index;
        return {
          layer: layer,
          layerIndex: index,
          latlng: e.leaflet_event.latlng,
          position: e.pixel,
          feature: e.feature.properties
        };
      } else {
        return {
          layer: this.lastLayer,
          layerIndex: this.lastLayerIndex
        };
      }
    },

    _createLeafletLayer: function () {
      var leafletLayer = new L.Layer();
      leafletLayer.onAdd = function () {};
      leafletLayer.onRemove = function () {};
      leafletLayer.setZIndex = function () {};
      return leafletLayer;
    },

    _onLayerAdded: function (layer, i) {
      var self = this;
      layer.bind('change:meta change:visible', function (e) {
        self.tangram.addLayer(e.attributes, (i + 1));
      });

      self.tangram.addLayer(layer.attributes, (i + 1));
    },

    _onURLsChanged: function (layerGroupModel) {
      var self = this;

      self.tangram.addDataSource(layerGroupModel.getTileURLTemplate('mvt'), layerGroupModel.getSubdomains());

      return function () {
        self.tangram.addDataSource(layerGroupModel.getTileURLTemplate('mvt'), layerGroupModel.getSubdomains());
      };
    }
  }
);

module.exports = LeafletCartoDBWebglLayerGroupView;
<commit_msg>Don't handle interactivity if there is no cartodb_id<commit_after>var log = require('cdb.log');
var _ = require('underscore');
var L = require('leaflet');
var TC = require('tangram.cartodb');
var LeafletLayerView = require('./leaflet-layer-view');
var Profiler = require('../../core/profiler');

var LeafletCartoDBWebglLayerGroupView = function (layerGroupModel, leafletMap, showLimitErrors) {
  var self = this;
  LeafletLayerView.apply(this, arguments);
  var metric = Profiler.metric('tangram.rendering');

  metric.start();

  this.trigger('loading');

  this.tangram = new TC(leafletMap, this.initConfig.bind(this, layerGroupModel), showLimitErrors);

  this.tangram.onLoaded(function () {
    if (metric) {
      self.trigger('load');
      metric.end();
      metric = void 0;

      log.info('Rendered Geometries Count: ', self.tangram.getTotalGeometries());
    }
  });

  this.layerGroupModel = layerGroupModel;
};

LeafletCartoDBWebglLayerGroupView.prototype = _.extend(
  {},
  LeafletLayerView.prototype,
  {
    initConfig: function (layerGroupModel) {
      var onURLsChanged = this._onURLsChanged(layerGroupModel);

      layerGroupModel.bind('change:urls', onURLsChanged);

      layerGroupModel.forEachGroupedLayer(this._onLayerAdded, this);
      layerGroupModel.onLayerAdded(this._onLayerAdded.bind(this));

      this._addInteractiveEvents();
      this._addErrorsEvents();
    },

    _addInteractiveEvents: function () {
      var hovered = false;
      var self = this;
      this.tangram.layer.setSelectionEvents({
        hover: function (e) {
          if (!e.feature || !e.feature.cartodb_id) {
            return;
          }

          if (e.feature) {
            hovered = true;
            self.trigger('featureOver', self._getFeatureObject(e));
          } else if (hovered) {
            hovered = false;
            self.trigger('featureOut', self._getFeatureObject(e));
          }
        },
        click: function (e) {
          if (e.feature && e.feature.cartodb_id) {
            self.trigger('featureClick', self._getFeatureObject(e));
          }
        }
      });
    },

    // This errors/warnings will come from Tangram. Bad thing is tangram doesn't
    // include error stus code, only error text, so we need to make some parsing
    // in order to segment limits errors (Too many requests)
    _addErrorsEvents: function () {
      this.tangram.scene.subscribe({
        tileError: function (error) {
          var code = parseInt(error.statusCode);

          switch (code) {
            case 429:
              this.layerGroupModel.addError({ type: 'limit' });
              break;
            case 204:
              // This error is thrown when the tile has no data
              break;
            default:
              this.layerGroupModel.addError({ type: 'tile' });
          }
        }.bind(this)
      });
    },

    _getFeatureObject: function (e) {
      var layer = this.layerGroupModel.getCartoLayerById(e.feature && e.feature.source_layer);
      if (layer) {
        var index = this.layerGroupModel.getIndexOfLayerInLayerGroup(layer);
        this.lastLayer = layer;
        this.lastLayerIndex = index;
        return {
          layer: layer,
          layerIndex: index,
          latlng: e.leaflet_event.latlng,
          position: e.pixel,
          feature: e.feature.properties
        };
      } else {
        return {
          layer: this.lastLayer,
          layerIndex: this.lastLayerIndex
        };
      }
    },

    _createLeafletLayer: function () {
      var leafletLayer = new L.Layer();
      leafletLayer.onAdd = function () {};
      leafletLayer.onRemove = function () {};
      leafletLayer.setZIndex = function () {};
      return leafletLayer;
    },

    _onLayerAdded: function (layer, i) {
      var self = this;
      layer.bind('change:meta change:visible', function (e) {
        self.tangram.addLayer(e.attributes, (i + 1));
      });

      self.tangram.addLayer(layer.attributes, (i + 1));
    },

    _onURLsChanged: function (layerGroupModel) {
      var self = this;

      self.tangram.addDataSource(layerGroupModel.getTileURLTemplate('mvt'), layerGroupModel.getSubdomains());

      return function () {
        self.tangram.addDataSource(layerGroupModel.getTileURLTemplate('mvt'), layerGroupModel.getSubdomains());
      };
    }
  }
);

module.exports = LeafletCartoDBWebglLayerGroupView;
<|endoftext|>


<commit_before>var util = require('util');
var events = require('events');
var stream = require('stream');
var fs = require('fs');
var path = require('path');
var url = require('url');

var RUN_SYNC = 0;
var RUN_ASYNC = 1;
var RUN_PATH = 2;

function WebKit(uri, opts, cb) {
	if (!(this instanceof WebKit)) return new WebKit(uri, opts, cb);
	if (!cb && typeof opts == "function") {
		cb = opts;
		opts = {};
	}
	if (typeof uri != "string") {
		opts = uri;
		uri = null;
	}
	if (!cb) cb = noop;
	opts = opts || {};
	this.pendingRequests = 0;
	this.looping = 0;
	this.ticket = 0;
	this.tickets = {};
	this.eventName = "webkitgtk" + Date.now();
	var self = this;
	this.on('error', function(msg, uri, line, column) {
		if (this.listeners('error').length <= 1) {
			console.error(msg, "\n", uri, "line", line, "column", column);
		}
	});
	this.display(opts.display || 0, opts, function(err, display) {
		if (err) return cb(err);
		process.env.DISPLAY = ":" + display;
		var Bindings = require(__dirname + '/lib/webkitgtk.node');
		self.webview = new Bindings({
			webextension: __dirname + '/lib/ext',
			eventName: self.eventName,
			requestListener: requestDispatcher.bind(self),
			responseListener: responseDispatcher.bind(self),
			eventsListener: eventsDispatcher.bind(self)
		});
		if (uri) self.load(uri, opts, cb);
	});
}
util.inherits(WebKit, events.EventEmitter);

function eventsDispatcher(err, json) {
	var obj = JSON.parse(json);
	if (!obj) {
		console.error("received invalid event", json);
		return;
	}
	if (obj.event) {
		obj.args.unshift(obj.event);
		this.emit.apply(this, obj.args);
	} else if (obj.ticket) {
		this.loop(false);
		var cb = this.tickets[obj.ticket];
		delete this.tickets[obj.ticket];
		cb(obj.error, obj.result);
	}
}

function Request(uri) {
	this.uri = uri;
}

function requestDispatcher(uri) {
	if (this.preloading && uri != this.webview.uri) {
		return;
	}
	var cancel = false;
	if (this.allow == "none") {
		if (uri != this.webview.uri) cancel = true;
	} else if (this.allow == "same-origin") {
		if (url.parse(uri).host != url.parse(this.webview.uri).host) cancel = true;
	} else if (this.allow instanceof RegExp) {
		if (!this.allow.test(uri)) cancel = true;
	}
	if (cancel) return;

	var req = new Request(uri);
	this.emit('request', req);
	if (req.uri) this.pendingRequests++;
	return req.uri;
}

function responseDispatcher(res) {
	if (this.preloading) return;
	this.pendingRequests--;
	this.emit('response', res);
}

function noop(err) {
	if (err) console.error(err);
}

WebKit.prototype.display = function(display, opts, cb) {
	var self = this;
	fs.exists('/tmp/.X' + display + '-lock', function(exists) {
		if (exists) return cb(null, display);
		if (display == 0) return cb("Error - do not spawn xvfb on DISPLAY 0");
		if (opts.xfb) {
			console.log("Unsafe xfb option is spawning xvfb...");
			require('headless')({
				display: {
					width: opts.xfb.width || 1024,
					height: opts.xfb.height || 768,
					depth: opts.xfb.depth || 32
				}
			}, display, function(err, child, display) {
				cb(err, display);
				if (!err) process.on('exit', function() {
					child.kill();
				});
			});
		}
	});
};

WebKit.prototype.load = function(uri, opts, cb) {
	if (!cb && typeof opts == "function") {
		cb = opts;
		opts = {};
	} else if (!opts) {
		opts = {};
	}
	if (!cb) cb = noop;
	this.allow = opts.allow || "all";
	var self = this;
	this.once('response', function(res) {
		var status = res.status;
		if (res.uri == self.webview.uri && (status < 200 || status >= 400)) {
			self.status = status;
		}
	});
	this.preload(uri, opts, cb);
	(function(next) {
		if (opts.stylesheet) {
			fs.readFile(opts.stylesheet, function(err, css) {
				if (err) console.error(err);
				if (opts.css) console.error("stylesheet option overwrites css option");
				if (css) opts.css = css;
				next();
			});
		} else {
			next();
		}
	})(function() {
		self.loop(true);
		self.webview.load(uri, opts, function(err) {
			self.loop(false);
			if (!self.preloading) {
				cb(err || self.status, self);
			}
			self.run(function(emit) {
				window.onerror = function() {
					emit.apply(null, Array.prototype.slice.call(arguments, 0));
				};
			}, "error");
			self.run(function(done) {
				// this function is executed in the window context of the current view - it cannot access local scopes
				if (/interactive|complete/.test(document.readyState)) done(null, document.readyState);
				else document.addEventListener('DOMContentLoaded', function() { done(null, "interactive"); }, false);
			}, function(err, result) {
				if (err) console.error(err);
				self.readyState = result;
				var prefix = self.preloading ? "pre" : "";
				self.emit(prefix + 'ready');
				if (result == "complete") {
					self.emit(prefix + 'load');
				}	else {
					self.run(function(done) {
						if (document.readyState == "complete") done(null, document.readyState);
						else window.addEventListener('load', function() { done(null, "complete"); }, false);
					}, function(err, result) {
						if (err) console.error(err);
						self.readyState = result;
						self.emit(prefix + 'load');
					});
				}
			});
		});
	});
	this.uri = uri;
	return this;
};

WebKit.prototype.unload = function(cb) {
	this.load('about:blank', cb);
	delete this.uri;
	delete this.status;
	delete this.readyState;
};

WebKit.prototype.close = function() {
	this.closed = true;
	if (this.timeoutId) {
		clearTimeout(this.timeoutId);
		delete this.timeoutId;
	}
	this.webview.close();
	delete this.webview;
	function disabled() {
		throw new Error("Cannot use a closed WebKit view");
	}
	for (var prop in WebKit.prototype) {
		this[prop] = disabled;
	}
};

WebKit.prototype.loop = function(start, block) {
	if (start) {
		this.looping++;
	} else if (start === false) {
		this.looping--;
	}
	if (!this.looping) return;
	var self = this;
	var busy = this.webview.loop(block);
	if (!busy && this.pendingRequests == 0 && !this.wasBusy && this.readyState == "complete") {
		setImmediate(function() {
			self.emit('idle');
		});
		this.looping--;
		return;
	} else {
		self.wasBusy = busy;
	}
	if (!self.timeoutId) self.timeoutId = setTimeout(function() {
		self.timeoutId = null;
		self.loop(null, block);
	}, 20);
};

WebKit.prototype.run = function(script, cb) {
	if (typeof script == "function") script = script.toString();
	cb = cb || noop;

	var message = {};
	if (typeof cb == "string") {
		message.event = cb;
	} else {
		message.ticket = (this.ticket++).toString();
		this.tickets[message.ticket] = cb;
	}

	var mode = RUN_SYNC;
	if (/^\s*function(\s+\w+)?\s*\(\s*\w+\s*\)/.test(script)) mode = RUN_ASYNC;
	else if (/^(file|http|https):/.test(script)) mode = RUN_PATH;

	message.mode = mode;

	// this is a hack because it leaks information between worlds
	// the good way of doing this is sending an empty event
	// then the webextension execute some JS to fetch the data that has
	// been stored somewhere as global TODO the day it doesn't work any more/:
	var dispatcher = '\
		var evt = document.createEvent("KeyboardEvent"); \
		evt.initKeyboardEvent("' + this.eventName + '", false, true, null, JSON.stringify(message)); \
		window.dispatchEvent(evt); \
		';
	var initialMessage = JSON.stringify(message);

	var self = this;
	setImmediate(function() {
		if (mode == RUN_SYNC) {
			if (/^\s*function(\s+\w+)?\s*\(\s*\)/.test(script)) script = '(' + script + ')()';
			else script = '(function() { return ' + script + '; })()';
			var wrap = '\
			(function() { \
				var message = ' + initialMessage + '; \
				try { \
					message.result = ' + script + '; \
				} catch(e) { \
					message.error = e; \
				} \
				' + dispatcher + '\
			})()';
			self.loop(true);
			self.webview.run(wrap, initialMessage);
		} else if (mode == RUN_ASYNC) {
			var fun = 'function(err, result) {\
				var message = ' + initialMessage + ';\
				if (message.event) { \
					message.args = Array.prototype.slice.call(arguments, 0); \
				} else { \
					if (err) message.error = err; \
					message.result = result; \
				} \
				' + dispatcher + '\
			}';
			var wrap = '(' + script + ')(' + fun + ');';
			self.loop(true);
			self.webview.run(wrap, initialMessage);
		} else if (mode == RUN_PATH) {
			console.log("TODO");
		}
	});
	return this;
};

function save(rstream, filename, cb) {
	cb = cb || noop;
	var wstream = fs.createWriteStream(filename);
	rstream.pipe(wstream).on('finish', cb).on('error', cb);
	return this;
}

WebKit.prototype.png = function() {
	var self = this;
	function close(err) {
		self.loop(false);
	}
	var passthrough = new stream.PassThrough();
	passthrough.save = save.bind(this, passthrough);
	if (!this.readyState || this.readyState == "loading") {
		this.on('load', function() {
			this.png().pipe(passthrough);
		});
	} else {
		this.loop(true, true);
		this.webview.png(function(err, buf) {
			if (err) {
				self.loop(false);
				passthrough.emit('error', err);
			}
			else if (buf == null) {
				self.loop(false);
				passthrough.end();
			} else {
				passthrough.write(buf);
			}
		});
	}
	return passthrough;
};

WebKit.prototype.html = function(cb) {
	if (!this.readyState || this.readyState == "loading") {
		this.on('ready', function() {
			this.html(cb);
		});
	} else {
		this.run("document.documentElement.outerHTML;", cb);
	}
	return this;
};

WebKit.prototype.pdf = function(filepath, opts, cb) {
	if (!cb && typeof opts == "function") {
		cb = opts;
		opts = {};
	} else if (!opts) {
		opts = {};
	}
	if (!cb) cb = noop;
	var self = this;
	if (!this.readyState || this.readyState == "loading") {
		this.on('load', function() {
			this.pdf(filepath, opts, cb);
		});
	} else {
		this.loop(true, true);
		this.webview.pdf("file://" + path.resolve(filepath), opts, function(err) {
			self.loop(false);
			cb(err);
		});
	}
	return this;
};

WebKit.prototype.preload = function(uri, opts, cb) {
	if (!opts.cookies || this.preloading !== undefined) return;
	this.preloading = true;
	this.once('preload', function() {
		var cookies = opts.cookies;
		if (!Array.isArray(cookies)) cookies = [cookies];
		var script = cookies.map(function(cookie) {
			return 'document.cookie = "' + cookie.replace(/"/g, '\\"') + '"';
		}).join(';') + ';';
		var self = this;
		this.run(script, function(err) {
			if (err) return cb(err);
			self.preloading = false;
			self.load(uri, opts, cb);
		});
	});
};

module.exports = WebKit;
<commit_msg>Stop loop immediately, stop loop in .close<commit_after>var util = require('util');
var events = require('events');
var stream = require('stream');
var fs = require('fs');
var path = require('path');
var url = require('url');

var RUN_SYNC = 0;
var RUN_ASYNC = 1;
var RUN_PATH = 2;

function WebKit(uri, opts, cb) {
	if (!(this instanceof WebKit)) return new WebKit(uri, opts, cb);
	if (!cb && typeof opts == "function") {
		cb = opts;
		opts = {};
	}
	if (typeof uri != "string") {
		opts = uri;
		uri = null;
	}
	if (!cb) cb = noop;
	opts = opts || {};
	this.pendingRequests = 0;
	this.looping = 0;
	this.ticket = 0;
	this.tickets = {};
	this.eventName = "webkitgtk" + Date.now();
	var self = this;
	this.on('error', function(msg, uri, line, column) {
		if (this.listeners('error').length <= 1) {
			console.error(msg, "\n", uri, "line", line, "column", column);
		}
	});
	this.display(opts.display || 0, opts, function(err, display) {
		if (err) return cb(err);
		process.env.DISPLAY = ":" + display;
		var Bindings = require(__dirname + '/lib/webkitgtk.node');
		self.webview = new Bindings({
			webextension: __dirname + '/lib/ext',
			eventName: self.eventName,
			requestListener: requestDispatcher.bind(self),
			responseListener: responseDispatcher.bind(self),
			eventsListener: eventsDispatcher.bind(self)
		});
		if (uri) self.load(uri, opts, cb);
	});
}
util.inherits(WebKit, events.EventEmitter);

function eventsDispatcher(err, json) {
	var obj = JSON.parse(json);
	if (!obj) {
		console.error("received invalid event", json);
		return;
	}
	if (obj.event) {
		obj.args.unshift(obj.event);
		this.emit.apply(this, obj.args);
	} else if (obj.ticket) {
		this.loop(false);
		var cb = this.tickets[obj.ticket];
		delete this.tickets[obj.ticket];
		cb(obj.error, obj.result);
	}
}

function Request(uri) {
	this.uri = uri;
}

function requestDispatcher(uri) {
	if (this.preloading && uri != this.webview.uri) {
		return;
	}
	var cancel = false;
	if (this.allow == "none") {
		if (uri != this.webview.uri) cancel = true;
	} else if (this.allow == "same-origin") {
		if (url.parse(uri).host != url.parse(this.webview.uri).host) cancel = true;
	} else if (this.allow instanceof RegExp) {
		if (!this.allow.test(uri)) cancel = true;
	}
	if (cancel) return;

	var req = new Request(uri);
	this.emit('request', req);
	if (req.uri) this.pendingRequests++;
	return req.uri;
}

function responseDispatcher(res) {
	if (this.preloading) return;
	this.pendingRequests--;
	this.emit('response', res);
}

function noop(err) {
	if (err) console.error(err);
}

WebKit.prototype.display = function(display, opts, cb) {
	var self = this;
	fs.exists('/tmp/.X' + display + '-lock', function(exists) {
		if (exists) return cb(null, display);
		if (display == 0) return cb("Error - do not spawn xvfb on DISPLAY 0");
		if (opts.xfb) {
			console.log("Unsafe xfb option is spawning xvfb...");
			require('headless')({
				display: {
					width: opts.xfb.width || 1024,
					height: opts.xfb.height || 768,
					depth: opts.xfb.depth || 32
				}
			}, display, function(err, child, display) {
				cb(err, display);
				if (!err) process.on('exit', function() {
					child.kill();
				});
			});
		}
	});
};

WebKit.prototype.load = function(uri, opts, cb) {
	if (!cb && typeof opts == "function") {
		cb = opts;
		opts = {};
	} else if (!opts) {
		opts = {};
	}
	if (!cb) cb = noop;
	this.allow = opts.allow || "all";
	var self = this;
	this.once('response', function(res) {
		var status = res.status;
		if (res.uri == self.webview.uri && (status < 200 || status >= 400)) {
			self.status = status;
		}
	});
	this.preload(uri, opts, cb);
	(function(next) {
		if (opts.stylesheet) {
			fs.readFile(opts.stylesheet, function(err, css) {
				if (err) console.error(err);
				if (opts.css) console.error("stylesheet option overwrites css option");
				if (css) opts.css = css;
				next();
			});
		} else {
			next();
		}
	})(function() {
		self.loop(true);
		self.webview.load(uri, opts, function(err) {
			self.loop(false);
			if (!self.preloading) {
				cb(err || self.status, self);
			}
			self.run(function(emit) {
				window.onerror = function() {
					emit.apply(null, Array.prototype.slice.call(arguments, 0));
				};
			}, "error");
			self.run(function(done) {
				// this function is executed in the window context of the current view - it cannot access local scopes
				if (/interactive|complete/.test(document.readyState)) done(null, document.readyState);
				else document.addEventListener('DOMContentLoaded', function() { done(null, "interactive"); }, false);
			}, function(err, result) {
				if (err) console.error(err);
				self.readyState = result;
				var prefix = self.preloading ? "pre" : "";
				self.emit(prefix + 'ready');
				if (result == "complete") {
					self.emit(prefix + 'load');
				}	else {
					self.run(function(done) {
						if (document.readyState == "complete") done(null, document.readyState);
						else window.addEventListener('load', function() { done(null, "complete"); }, false);
					}, function(err, result) {
						if (err) console.error(err);
						self.readyState = result;
						self.emit(prefix + 'load');
					});
				}
			});
		});
	});
	this.uri = uri;
	return this;
};

WebKit.prototype.unload = function(cb) {
	this.load('about:blank', cb);
	delete this.uri;
	delete this.status;
	delete this.readyState;
};

WebKit.prototype.close = function() {
	this.closed = true;
	this.loop(false);
	this.webview.close();
	delete this.webview;
	function disabled() {
		throw new Error("Cannot use a closed WebKit view");
	}
	for (var prop in WebKit.prototype) {
		this[prop] = disabled;
	}
};

WebKit.prototype.loop = function(start, block) {
	if (start) {
		this.looping++;
	} else if (start === false) {
		this.looping--;
	}
	if (!this.looping) {
		if (this.timeoutId) {
			clearTimeout(this.timeoutId);
			this.timeoutId = null;
		}
		return;
	}
	var self = this;
	var busy = this.webview.loop(block);
	if (!busy && this.pendingRequests == 0 && !this.wasBusy && this.readyState == "complete") {
		setImmediate(function() {
			self.emit('idle');
		});
		this.looping--;
		return;
	} else {
		self.wasBusy = busy;
	}
	if (!self.timeoutId) self.timeoutId = setTimeout(function() {
		self.timeoutId = null;
		self.loop(null, block);
	}, 20);
};

WebKit.prototype.run = function(script, cb) {
	if (typeof script == "function") script = script.toString();
	cb = cb || noop;

	var message = {};
	if (typeof cb == "string") {
		message.event = cb;
	} else {
		message.ticket = (this.ticket++).toString();
		this.tickets[message.ticket] = cb;
	}

	var mode = RUN_SYNC;
	if (/^\s*function(\s+\w+)?\s*\(\s*\w+\s*\)/.test(script)) mode = RUN_ASYNC;
	else if (/^(file|http|https):/.test(script)) mode = RUN_PATH;

	message.mode = mode;

	// this is a hack because it leaks information between worlds
	// the good way of doing this is sending an empty event
	// then the webextension execute some JS to fetch the data that has
	// been stored somewhere as global TODO the day it doesn't work any more/:
	var dispatcher = '\
		var evt = document.createEvent("KeyboardEvent"); \
		evt.initKeyboardEvent("' + this.eventName + '", false, true, null, JSON.stringify(message)); \
		window.dispatchEvent(evt); \
		';
	var initialMessage = JSON.stringify(message);

	var self = this;
	setImmediate(function() {
		if (mode == RUN_SYNC) {
			if (/^\s*function(\s+\w+)?\s*\(\s*\)/.test(script)) script = '(' + script + ')()';
			else script = '(function() { return ' + script + '; })()';
			var wrap = '\
			(function() { \
				var message = ' + initialMessage + '; \
				try { \
					message.result = ' + script + '; \
				} catch(e) { \
					message.error = e; \
				} \
				' + dispatcher + '\
			})()';
			self.loop(true);
			self.webview.run(wrap, initialMessage);
		} else if (mode == RUN_ASYNC) {
			var fun = 'function(err, result) {\
				var message = ' + initialMessage + ';\
				if (message.event) { \
					message.args = Array.prototype.slice.call(arguments, 0); \
				} else { \
					if (err) message.error = err; \
					message.result = result; \
				} \
				' + dispatcher + '\
			}';
			var wrap = '(' + script + ')(' + fun + ');';
			self.loop(true);
			self.webview.run(wrap, initialMessage);
		} else if (mode == RUN_PATH) {
			console.log("TODO");
		}
	});
	return this;
};

function save(rstream, filename, cb) {
	cb = cb || noop;
	var wstream = fs.createWriteStream(filename);
	rstream.pipe(wstream).on('finish', cb).on('error', cb);
	return this;
}

WebKit.prototype.png = function() {
	var self = this;
	function close(err) {
		self.loop(false);
	}
	var passthrough = new stream.PassThrough();
	passthrough.save = save.bind(this, passthrough);
	if (!this.readyState || this.readyState == "loading") {
		this.on('load', function() {
			this.png().pipe(passthrough);
		});
	} else {
		this.loop(true, true);
		this.webview.png(function(err, buf) {
			if (err) {
				self.loop(false);
				passthrough.emit('error', err);
			}
			else if (buf == null) {
				self.loop(false);
				passthrough.end();
			} else {
				passthrough.write(buf);
			}
		});
	}
	return passthrough;
};

WebKit.prototype.html = function(cb) {
	if (!this.readyState || this.readyState == "loading") {
		this.on('ready', function() {
			this.html(cb);
		});
	} else {
		this.run("document.documentElement.outerHTML;", cb);
	}
	return this;
};

WebKit.prototype.pdf = function(filepath, opts, cb) {
	if (!cb && typeof opts == "function") {
		cb = opts;
		opts = {};
	} else if (!opts) {
		opts = {};
	}
	if (!cb) cb = noop;
	var self = this;
	if (!this.readyState || this.readyState == "loading") {
		this.on('load', function() {
			this.pdf(filepath, opts, cb);
		});
	} else {
		this.loop(true, true);
		this.webview.pdf("file://" + path.resolve(filepath), opts, function(err) {
			self.loop(false);
			cb(err);
		});
	}
	return this;
};

WebKit.prototype.preload = function(uri, opts, cb) {
	if (!opts.cookies || this.preloading !== undefined) return;
	this.preloading = true;
	this.once('preload', function() {
		var cookies = opts.cookies;
		if (!Array.isArray(cookies)) cookies = [cookies];
		var script = cookies.map(function(cookie) {
			return 'document.cookie = "' + cookie.replace(/"/g, '\\"') + '"';
		}).join(';') + ';';
		var self = this;
		this.run(script, function(err) {
			if (err) return cb(err);
			self.preloading = false;
			self.load(uri, opts, cb);
		});
	});
};

module.exports = WebKit;
<|endoftext|>


<commit_before>import { isBlank, isPresent } from '@ember/utils';
import { A } from '@ember/array';
import EmberObject, { computed, get } from '@ember/object';
import Component from '@ember/component';

export default Component.extend({
  router: null,
  applicationController: null,

  handlerInfos: computed("applicationController.currentPath", function() {
    var router = this.get("router")._routerMicrolib || this.get("router").router;
    return router.currentHandlerInfos;
  }),

  /*
    For the pathNames and controllers properties, we must be careful not to NOT
    specify the properties of the route in our dependent keys.

    Observing the controller property of the route causes some serious problems:
    https://github.com/chrisfarber/ember-breadcrumbs/issues/21
  */

  pathNames: computed("handlerInfos.[]", function() {
    return this.get("handlerInfos").map(function(handlerInfo) {
      return handlerInfo.name;
    });
  }),

  controllers: computed("handlerInfos.[]", function() {
    return this.get("handlerInfos").map(function(handlerInfo) {
      return handlerInfo.handler.controller;
    });
  }),

  breadCrumbs: computed("controllers.@each{breadCrumbs,breadCrumb,breadCrumbPath,breadCrumbModel}",
    "pathNames.[]", function() {
    var controllers = this.get("controllers");
    var defaultPaths = this.get("pathNames");
    var breadCrumbs = A([]);

    controllers.forEach(function(controller, index) {
      var crumbs = controller.get("breadCrumbs") || A([]);
      var singleCrumb = controller.get("breadCrumb");

      if (!isBlank(singleCrumb)) {
        crumbs.push({
          label: singleCrumb,
          path: controller.get("breadCrumbPath"),
          model: controller.get("breadCrumbModel"),
        });
      }

      crumbs.forEach(function (crumb) {
        breadCrumbs.addObject(EmberObject.create({
          label: crumb.label,
          path: crumb.path || defaultPaths[index],
          model: crumb.model,
          linkable: isPresent(crumb.linkable) ? crumb.linkable : crumb.path !== false,
          isCurrent: false
        }));
      });
    });

    var deepestCrumb = get(breadCrumbs, "lastObject");
    if (deepestCrumb) {
      deepestCrumb.isCurrent = true;
    }

    return breadCrumbs;
  })
});
<commit_msg>Fix missing dot<commit_after>import { isBlank, isPresent } from '@ember/utils';
import { A } from '@ember/array';
import EmberObject, { computed, get } from '@ember/object';
import Component from '@ember/component';

export default Component.extend({
  router: null,
  applicationController: null,

  handlerInfos: computed("applicationController.currentPath", function() {
    var router = this.get("router")._routerMicrolib || this.get("router").router;
    return router.currentHandlerInfos;
  }),

  /*
    For the pathNames and controllers properties, we must be careful not to NOT
    specify the properties of the route in our dependent keys.

    Observing the controller property of the route causes some serious problems:
    https://github.com/chrisfarber/ember-breadcrumbs/issues/21
  */

  pathNames: computed("handlerInfos.[]", function() {
    return this.get("handlerInfos").map(function(handlerInfo) {
      return handlerInfo.name;
    });
  }),

  controllers: computed("handlerInfos.[]", function() {
    return this.get("handlerInfos").map(function(handlerInfo) {
      return handlerInfo.handler.controller;
    });
  }),

  breadCrumbs: computed("controllers.@each.{breadCrumbs,breadCrumb,breadCrumbPath,breadCrumbModel}",
    "pathNames.[]", function() {
    var controllers = this.get("controllers");
    var defaultPaths = this.get("pathNames");
    var breadCrumbs = A([]);

    controllers.forEach(function(controller, index) {
      var crumbs = controller.get("breadCrumbs") || A([]);
      var singleCrumb = controller.get("breadCrumb");

      if (!isBlank(singleCrumb)) {
        crumbs.push({
          label: singleCrumb,
          path: controller.get("breadCrumbPath"),
          model: controller.get("breadCrumbModel"),
        });
      }

      crumbs.forEach(function (crumb) {
        breadCrumbs.addObject(EmberObject.create({
          label: crumb.label,
          path: crumb.path || defaultPaths[index],
          model: crumb.model,
          linkable: isPresent(crumb.linkable) ? crumb.linkable : crumb.path !== false,
          isCurrent: false
        }));
      });
    });

    var deepestCrumb = get(breadCrumbs, "lastObject");
    if (deepestCrumb) {
      deepestCrumb.isCurrent = true;
    }

    return breadCrumbs;
  })
});
<|endoftext|>


<commit_before>import Component from '@ember/component';
import { copy } from 'ember-copy';
import { assert } from '@ember/debug';
import { deprecate } from '@ember/application/deprecations';
import { typeOf, isPresent } from '@ember/utils';
import {
  setProperties,
  getWithDefault,
  computed,
  get
} from '@ember/object';
import { getOwner } from '@ember/application';
import { A as emberArray } from '@ember/array';
import { classify } from '@ember/string';
import layout from '../templates/components/bread-crumbs';
import { inject as service } from '@ember/service';

const {
  bool,
  readOnly
} = computed;

export default Component.extend({
  routerService: service('router'),
  layout,
  tagName: 'ol',
  linkable: true,
  reverse: false,
  classNameBindings: ['breadCrumbClass'],
  hasBlock: bool('template').readOnly(),
  currentUrl: readOnly('routerService.currentURL'),
  currentRouteName: readOnly('routerService.currentRouteName'),

  routeHierarchy: computed('currentUrl', 'currentRouteName', 'reverse', {
    get() {
      get(this, 'currentUrl');
      const currentRouteName = getWithDefault(this, 'currentRouteName', false);

      assert('[ember-crumbly] Could not find a current route', currentRouteName);

      const routeNames = currentRouteName.split('.');
      const filteredRouteNames = this._filterIndexAndLoadingRoutes(routeNames);
      const crumbs = this._lookupBreadCrumb(routeNames, filteredRouteNames);

      return get(this, 'reverse') ? crumbs.reverse() : crumbs;
    }
  }).readOnly(),

  breadCrumbClass: computed('outputStyle', {
    get() {
      let className = 'breadcrumb';
      const outputStyle = getWithDefault(this, 'outputStyle', '');
      if (isPresent(outputStyle)) {
        deprecate('outputStyle option will be deprecated in the next major release', false, { id: 'ember-crumbly.outputStyle', until: '2.0.0' });
      }
      const lowerCaseOutputStyle = outputStyle.toLowerCase();

      if (lowerCaseOutputStyle === 'foundation') {
        className = 'breadcrumbs';
      }

      return className;
    }
  }).readOnly(),

  _guessRoutePath(routeNames, name, index) {
    const routes = routeNames.slice(0, index + 1);

    if (routes.length === 1) {
      let path = `${name}.index`;

      return (this._lookupRoute(path)) ? path : name;
    }

    return routes.join('.');
  },

  _filterIndexAndLoadingRoutes(routeNames) {
    return routeNames.filter((name) => !(name === 'index' || name === 'loading'));
  },

  _lookupRoute(routeName) {
    return getOwner(this).lookup(`route:${routeName}`);
  },

  _lookupBreadCrumb(routeNames, filteredRouteNames) {
    const defaultLinkable = get(this, 'linkable');
    const pathLength = filteredRouteNames.length;
    const breadCrumbs = emberArray();

    filteredRouteNames.map((name, index) => {
      let path = this._guessRoutePath(routeNames, name, index);
      const route = this._lookupRoute(path);
      const isHead = index === 0;
      const isTail = index === pathLength - 1;

      const crumbLinkable = (index === pathLength - 1) ? false : defaultLinkable;

      assert(`[ember-crumbly] \`route:${path}\` was not found`, route);

      const multipleBreadCrumbs = route.get('breadCrumbs');

      if (multipleBreadCrumbs) {
        multipleBreadCrumbs.forEach((breadCrumb) => {
          breadCrumbs.pushObject(breadCrumb);
        });
      } else {
        let breadCrumb = copy(getWithDefault(route, 'breadCrumb', {
          title: classify(name)
        }));

        if (typeOf(breadCrumb) === 'null') {
          return;
        } else {
          if (isPresent(breadCrumb.path)) {
            path = breadCrumb.path;
          }

          setProperties(breadCrumb, {
            path,
            isHead,
            isTail,
            linkable: breadCrumb.hasOwnProperty('linkable') ? breadCrumb.linkable : crumbLinkable
          });
        }

        breadCrumbs.pushObject(breadCrumb);
      }
    });

    return emberArray(breadCrumbs.filter((breadCrumb) => typeOf(breadCrumb) !== 'undefined'));
  }
});
<commit_msg>Engines support (#143)<commit_after>import Component from '@ember/component';
import { copy } from 'ember-copy';
import { assert } from '@ember/debug';
import { deprecate } from '@ember/application/deprecations';
import { typeOf, isPresent } from '@ember/utils';
import {
  setProperties,
  getWithDefault,
  computed,
  get
} from '@ember/object';
import { getOwner } from '@ember/application';
import { A as emberArray } from '@ember/array';
import { classify } from '@ember/string';
import layout from '../templates/components/bread-crumbs';
import { inject as service } from '@ember/service';

const {
  bool,
  readOnly
} = computed;

export default Component.extend({
  layout,
  tagName: 'ol',
  linkable: true,
  reverse: false,
  classNameBindings: ['breadCrumbClass'],
  hasBlock: bool('template').readOnly(),
  routing: service('-routing'),
  currentUrl: readOnly('applicationRoute.router.url'),
  currentRouteName: readOnly('applicationRoute.controller.currentRouteName'),

  routeHierarchy: computed('currentUrl', 'currentRouteName', 'reverse', {
    get() {
      get(this, 'currentUrl');
      const currentRouteName = getWithDefault(this, 'currentRouteName', false);

      assert('[ember-crumbly] Could not find a current route', currentRouteName);

      const routeNames = currentRouteName.split('.');
      const filteredRouteNames = this._filterIndexAndLoadingRoutes(routeNames);
      const crumbs = this._lookupBreadCrumb(routeNames, filteredRouteNames);

      return get(this, 'reverse') ? crumbs.reverse() : crumbs;
    }
  }).readOnly(),

  breadCrumbClass: computed('outputStyle', {
    get() {
      let className = 'breadcrumb';
      const outputStyle = getWithDefault(this, 'outputStyle', '');
      if (isPresent(outputStyle)) {
        deprecate('outputStyle option will be deprecated in the next major release', false, { id: 'ember-crumbly.outputStyle', until: '2.0.0' });
      }
      const lowerCaseOutputStyle = outputStyle.toLowerCase();

      if (lowerCaseOutputStyle === 'foundation') {
        className = 'breadcrumbs';
      }

      return className;
    }
  }).readOnly(),

  _guessRoutePath(routeNames, name, index) {
    const routes = routeNames.slice(0, index + 1);

    if (routes.length === 1) {
      let path = `${name}.index`;

      return (this._lookupRoute(path)) ? path : name;
    }

    return routes.join('.');
  },

  _filterIndexAndLoadingRoutes(routeNames) {
    return routeNames.filter((name) => !(name === 'index' || name === 'loading'));
  },

  /*
   * Lookup local route first and fallback to engine,
   * I'm not exactly familiar with local vs engine routes,
   * but my thinking is you should be able to override an
   * engine route locally so it should take priority.
   * I could be totally wrong here...
   */
   _lookupRoute(routeName) {
     return this._lookupLocalRoute(routeName) || this._lookupEngineRoute(routeName);
   },

   _lookupLocalRoute(routeName) {
     return getOwner(this).lookup(`route:${routeName}`);
   },

   _lookupEngineRoute(routeName) {
     const router = get(this, 'routing.router');

     let engineInfo = router._engineInfoByRoute[routeName];

     if (!engineInfo) {
       return;
     }

     return router
       ._getEngineInstance(engineInfo)
       .lookup(`route:${engineInfo.localFullName}`);
   },

  _lookupBreadCrumb(routeNames, filteredRouteNames) {
    const defaultLinkable = get(this, 'linkable');
    const pathLength = filteredRouteNames.length;
    const breadCrumbs = emberArray();

    filteredRouteNames.map((name, index) => {
      let path = this._guessRoutePath(routeNames, name, index);
      const route = this._lookupRoute(path);
      const isHead = index === 0;
      const isTail = index === pathLength - 1;

      const crumbLinkable = (index === pathLength - 1) ? false : defaultLinkable;

      assert(`[ember-crumbly] \`route:${path}\` was not found`, route);

      const multipleBreadCrumbs = route.get('breadCrumbs');

      if (multipleBreadCrumbs) {
        multipleBreadCrumbs.forEach((breadCrumb) => {
          breadCrumbs.pushObject(breadCrumb);
        });
      } else {
        let breadCrumb = copy(getWithDefault(route, 'breadCrumb', {
          title: classify(name)
        }));

        if (typeOf(breadCrumb) === 'null') {
          return;
        } else {
          if (isPresent(breadCrumb.path)) {
            path = breadCrumb.path;
          }

          setProperties(breadCrumb, {
            path,
            isHead,
            isTail,
            linkable: breadCrumb.hasOwnProperty('linkable') ? breadCrumb.linkable : crumbLinkable
          });
        }

        breadCrumbs.pushObject(breadCrumb);
      }
    });

    return emberArray(breadCrumbs.filter((breadCrumb) => typeOf(breadCrumb) !== 'undefined'));
  }
});
<|endoftext|>


<commit_before>/* globals define */
var charming = function() {

  'use strict';

  return function(elem, opts) {

    opts = opts || {};
    var tagName = opts.tagName || 'span',
        classPrefix = typeof opts.classPrefix !== 'undefined' ? opts.classPrefix : 'char',
        count = 1;

    var traverse = function(elem) {
      var childNodes = Array.prototype.slice.call(elem.childNodes); // static array of nodes
      for (var i = 0, len = childNodes.length; i < len; i++) {
        traverse(childNodes[i]);
      }
      if (elem.nodeType === Node.TEXT_NODE) {
        inject(elem);
      }
    };

    var inject = function(elem) {
      var str = elem.nodeValue,
          parentNode = elem.parentNode;
      for (var i = 0, len = str.length; i < len; i++) {
        var node = document.createElement(tagName);
        if (classPrefix) {
          node.className = classPrefix + count;
          count++;
        }
        node.appendChild(document.createTextNode(str[i]));
        parentNode.insertBefore(node, elem);
      }
      parentNode.removeChild(elem);
    };

    traverse(elem);

    return elem;

  };

};

(function(root, factory) {
  if (typeof define === 'function' && define.amd) { // istanbul ignore
    define(factory);
  } else if (typeof exports === 'object') { // istanbul ignore
    module.exports = factory;
  } else {
    root.charming = factory(root);
  }
})(this, charming);
<commit_msg>Refactor + remove AMD from boilerplate<commit_after>(function(fn) {
  /* istanbul ignore else  */
  if (typeof module === 'undefined') {
    this.charming = fn;
  } else {
    module.exports = fn;
  }
})(function(elem, opts) {

  'use strict';

  opts = opts || {};
  var tagName = opts.tagName || 'span';
  var classPrefix = opts.classPrefix != null ? opts.classPrefix : 'char';

  var count = 1;

  var inject = function(elem) {
    var parentNode = elem.parentNode;
    var str = elem.nodeValue;
    var len = str.length;
    var i = -1;
    while (++i < len) {
      var node = document.createElement(tagName);
      if (classPrefix) {
        node.className = classPrefix + count;
        count++;
      }
      node.appendChild(document.createTextNode(str[i]));
      parentNode.insertBefore(node, elem);
    }
    parentNode.removeChild(elem);
  };

  (function traverse(elem) {
    var childNodes = [].slice.call(elem.childNodes); // static array of nodes
    var len = childNodes.length;
    var i = -1;
    while (++i < len) {
      traverse(childNodes[i]);
    }
    if (elem.nodeType === Node.TEXT_NODE) {
      inject(elem);
    }
  })(elem);

  return elem;

});
<|endoftext|>


<commit_before>/*jshint loopfunc: true */

import Ember from 'ember';
import InboundActions from 'ember-component-inbound-actions/inbound-actions';
import EmberJstreeActions from 'ember-cli-jstree/mixins/ember-jstree-actions';

export default Ember.Component.extend(InboundActions, EmberJstreeActions, {

    // Properties for Ember communication
    actionReceiver:       null,
    currentNode:          null,
    selectedNodes:        null,

    // Basic configuration objects
    data:                 null,
    plugins:              null,
    themes:               null,
    checkCallback:        true,
    multiple:             true,

    // Refresh configuration variables
    skipLoading:          false,
    forgetState:          false,

    // Plugin option objects
    checkboxOptions:      null,
    contextmenuOptions:   null,
    typesOptions:         null,
    searchOptions:        null,
    dndOptions:           null,

    selectionDidChange:   null,
    treeObject:           null,

    // Internals
    _isDestroying:        false,

    isReady:              false,
    _searchTerm:          null,

    _isReadyTestWaiter() {
        return this.get('isReady') === true;
    },

    didInsertElement() {
        Ember.run.schedule('afterRender', this, this.createTree);
    },

    createTree() {
        if (Ember.testing) {
            // Add test waiter.
            Ember.Test.registerWaiter(this, this._isReadyTestWaiter);
        }

        let treeObject = this._setupJsTree();

        this._setupEventHandlers(treeObject);

        this.set('treeObject', treeObject);
    },

    willDestroyElement() {
        if (Ember.testing) {
            Ember.Test.unregisterWaiter(this, this._isReadyTestWaiter);
        }

        this.set('isReady', false);
        this.set('_isDestroying', true);
        this.send('destroy');
    },

    didUpdateAttrs() {
        this._super(...arguments);

        let pluginsArray = this.get('plugins');
        if (Ember.isPresent(pluginsArray)) {
            let searchOptions = this.get('searchOptions');
            if (Ember.isPresent(searchOptions) && pluginsArray.includes('search')) {
                let searchTerm = this.get('searchTerm');
                if (this.get('_searchTerm') !== searchTerm) {
                    Ember.run.next('afterRender', () => {
                        this.set('_searchTerm', searchTerm);
                        this.getTree().search(searchTerm);
                    });
                }
            }
        }
    },

    /**
     * Main setup function that registers all the plugins and sets up the core
     * configuration object for jsTree
     *
     * @method _setupJsTree
     */
    _setupJsTree() {
        return this.$().jstree(this._buildConfig());
    },

    /**
     * Builds config object for jsTree. Could be used to override config in descendant classes.
     *
     * @method _buildConfig
     */
    _buildConfig() {
        let configObject = {};

        configObject['core'] = {
            'data': this.get('data'),
            'check_callback': this.get('checkCallback'),
            'multiple': this.get('multiple')
        };

        let themes = this.get('themes');
        if (Ember.isPresent(themes) && Ember.typeOf(themes) === 'object') {
            configObject['core']['themes'] = themes;
        }

        let pluginsArray = this.get('plugins');
        if (Ember.isPresent(pluginsArray)) {
            pluginsArray = pluginsArray.replace(/ /g, '').split(',');
            configObject['plugins'] = pluginsArray;

            if (pluginsArray.includes('contextmenu') ||
                pluginsArray.includes('dnd') ||
                pluginsArray.includes('unique')) {
                // These plugins need core.check_callback
                configObject['core']['check_callback'] = configObject['core']['check_callback'] || true;
            }

            let checkboxOptions = this.get('checkboxOptions');
            if (Ember.isPresent(checkboxOptions) && pluginsArray.includes('checkbox')) {
                configObject['checkbox'] = checkboxOptions;
            }

            let searchOptions = this.get('searchOptions');
            if (Ember.isPresent(searchOptions) && pluginsArray.includes('search')) {
                configObject['search'] = searchOptions;
            }

            let stateOptions = this.get('stateOptions');
            if (Ember.isPresent(stateOptions) && pluginsArray.includes('state')) {
                configObject['state'] = stateOptions;
            }

            let typesOptions = this.get('typesOptions');
            if (Ember.isPresent(typesOptions) && pluginsArray.includes('types')) {
                configObject['types'] = typesOptions;
            }

            let contextmenuOptions = this.get('contextmenuOptions');
            if (Ember.isPresent(contextmenuOptions) && pluginsArray.includes('contextmenu')) {
                configObject['contextmenu'] = this._setupContextMenus(contextmenuOptions);
            }

            let dndOptions = this.get('dndOptions');
            if (Ember.isPresent(dndOptions) && pluginsArray.includes('dnd')) {
                configObject['dnd'] = dndOptions;
            }
        }

        return configObject;
    },

    /**
     * Setup context menu action handlers to handle ember actions
     *
     * @method _setupContextMenus
     * @param  {Array} contextmenuOptions Context menu configuration options
     * @return {Array} An Array of Ember-friendly options to pass back into the config object
     */
    _setupContextMenus(contextmenuOptions) {
        if (Ember.typeOf(contextmenuOptions['items']) === 'object') {
            let newMenuItems = {};
            let menuItems = Object.keys(contextmenuOptions['items']);
            for (let menuItem of menuItems) {
                let itemData = contextmenuOptions['items'][menuItem];
                newMenuItems[menuItem] = itemData;

                // Only change if not a function
                // This needs to be done to handle Ember actions
                if (Ember.typeOf(itemData['action']) !== 'function') {
                    let emberAction = itemData['action'];

                    newMenuItems[menuItem]['action'] = (data) => {
                        this.send("contextmenuItemDidClick", emberAction, data);
                    };
                }
            }

            contextmenuOptions['items'] = newMenuItems;
        }

        return contextmenuOptions;
    },

    /**
     * Register all sorts of events
     * TODO: This should eventually encompass all of the jsTree events declared in their API.
     *
     * @method _setupEventHandlers
     * @param  {Object}
     * @return
     */
    _setupEventHandlers(treeObject) {

        if (typeof treeObject !== 'object') {
            throw new Error('You must pass a valid jsTree object to set up its event handlers');
        }

        /*
          Event: init.jstree
          Action: eventDidInit
          triggered after all events are bound
        */
        treeObject.on('init.jstree', () => {
            Ember.run(this, function() {
                if (this.get('isDestroyed') || this.get('isDestroying')) {
                    return;
                }
                this.sendAction('eventDidInit');
            });
        });

        /*
          Event: loading.jstree
          Action: eventIsLoading
          triggered after the loading text is shown and before loading starts
        */
        treeObject.on('loading.jstree', () => {
            Ember.run(this, function() {
                if (this.get('isDestroyed') || this.get('isDestroying')) {
                    return;
                }
                this.sendAction('eventIsLoading');
            });
        });

        /*
          Event: loaded.jstree
          Action: eventDidLoad
          triggered after the root node is loaded for the first time
        */
        treeObject.on('loaded.jstree', () => {
            Ember.run(this, function() {
                if (this.get('isDestroyed') || this.get('isDestroying')) {
                    return;
                }
                this.sendAction('eventDidLoad');
            });
        });

        /*
          Event: ready.jstree
          Action: eventDidBecomeReady
          triggered after all nodes are finished loading
        */
        treeObject.on('ready.jstree', () => {
            Ember.run(this, function() {
                if (this.get('isDestroyed') || this.get('isDestroying')) {
                    return;
                }
                this.set('isReady', true);
                this.sendAction('eventDidBecomeReady');
            });
        });

        /*
          Event: redraw.jstree
          Action: eventDidRedraw
          triggered after nodes are redrawn
        */
        treeObject.on('redraw.jstree', () => {
            Ember.run(this, function() {
                if (this.get('isDestroyed') || this.get('isDestroying')) {
                    return;
                }
                this.sendAction('eventDidRedraw');
            });
        });

        /*
          Event: after_open.jstree
          Action: eventDidOpen
          triggered when a node is opened and the animation is complete
        */
        treeObject.on('after_open.jstree', (event, data) => {
            Ember.run(this, function() {
                if (this.get('isDestroyed') || this.get('isDestroying')) {
                    return;
                }
                this.sendAction('eventDidOpen', data.node);
            });
        });

        /*
          Event: after_close.jstree
          Action: eventDidClose
          triggered when a node is closed and the animation is complete
        */
        treeObject.on('after_close.jstree', (event, data) => {
            Ember.run(this, function() {
                if (this.get('isDestroyed') || this.get('isDestroying')) {
                    return;
                }
                this.sendAction('eventDidClose', data.node);
            });
        });

        /*
          Event: select_node.jstree
          Action: eventDidSelectNode
          triggered when an node is selected
        */
        treeObject.on('select_node.jstree', (event, data) => {
            Ember.run(this, function() {
                if (this.get('isDestroyed') || this.get('isDestroying')) {
                    return;
                }
                this.sendAction('eventDidSelectNode', data.node, data.selected, data.event);
            });
        });

        /*
          Event: deselect_node.jstree
          Action: eventDidDeelectNode
          triggered when an node is deselected
        */
        treeObject.on('deselect_node.jstree', (event, data) => {
            Ember.run(this, function() {
                if (this.get('isDestroyed') || this.get('isDestroying')) {
                    return;
                }
                this.sendAction('eventDidDeselectNode', data.node, data.selected, data.event);
            });
        });

        /*
          Event: changed.jstree
          Action: jstreeDidChange
          triggered when selection changes
        */
        treeObject.on('changed.jstree', (event, data) => {
            Ember.run(this, function() {
                if (this.get('isDestroyed') || this.get('isDestroying')) {
                    return;
                }

                // Check if selection changed
                if (Ember.isPresent(this.get('treeObject'))) {
                    let selectionChangedEventNames = [
                        'model',
                        'select_node',
                        'deselect_node',
                        'select_all',
                        'deselect_all'
                    ];
                    if (Ember.isPresent(data.action) && selectionChangedEventNames.includes(data.action)) {
                        let selNodes = Ember.A(this.get('treeObject').jstree(true).get_selected(true));
                        this.set('selectedNodes', selNodes);
                    }
                }

                this.sendAction('eventDidChange', data);
            });
        });

        /*
          Event: hover_node.jstree
          Action: eventDidHoverNode
          triggered when a node is hovered
        */
        treeObject.on('hover_node.jstree', (event, data) => {
            Ember.run(this, function() {
                if (this.get('isDestroyed') || this.get('isDestroying')) {
                    return;
                }
                this.sendAction('eventDidHoverNode', data.node);
            });
        });

        /*
          Event: dehover_node.jstree
          Action: eventDidDehoverNode
          triggered when a node is no longer hovered
        */
        treeObject.on('dehover_node.jstree', (event, data) => {
            Ember.run(this, function() {
                if (this.get('isDestroyed') || this.get('isDestroying')) {
                    return;
                }
                this.sendAction('eventDidDehoverNode', data.node);
            });
        });

        /*
          Event: show_node.jstree
          Action: eventDidShowNode
          triggered when a node is no longer hovered
        */
        treeObject.on('show_node.jstree', (event, data) => {
            Ember.run(this, function() {
                if (this.get('isDestroyed') || this.get('isDestroying')) {
                    return;
                }
                this.sendAction('eventDidShowNode', data.node);
            });
        });

        /*
          Event: move_node.jstree
          Action: eventDidMoveNode
          triggered when a node is moved
        */
        treeObject.on('move_node.jstree', (event, data) => {
            Ember.run(this, function() {
                if (this.get('isDestroyed') || this.get('isDestroying')) {
                    return;
                }
                this.sendAction('eventDidMoveNode', data);
            });
        });

      if (this.get('plugins').indexOf('checkbox') > -1) {

        /*
           Event: disable_checkbox.jstree
           Action: eventDidDisableCheckbox
           triggered when an node's checkbox is disabled
         */
        treeObject.on('disable_checkbox.jstree', (event, data) => {
          Ember.run(this, function() {
            if (this.get('isDestroyed') || this.get('isDestroying')) {
              return;
            }
            this.sendAction('eventDidDisableCheckbox', data.node);
          });
        });

        /*
           Event: enable_checkbox.jstree
           Action: eventDidEnableCheckbox
           triggered when an node's checkbox is enabled
         */
        treeObject.on('enable_checkbox.jstree', (event, data) => {
          Ember.run(this, function() {
            if (this.get('isDestroyed') || this.get('isDestroying')) {
              return;
            }
            this.sendAction('eventDidEnableCheckbox', data.node);
          });
        });

        if (Ember.isPresent('checkboxOptions.tie_selected') && !this.get('checkboxOptions.tie_selected')) {
          // let checkedNodes = Ember.A(this.get('treeObject').jstree(true).get_checked(true));
          // this.set('checkedNodes', checkedNodes);

          /*
             Event: check_node.jstree
             Action: eventDidCheckNode
             triggered when an node is checked (only if tie_selection in checkbox settings is false)
           */
          treeObject.on('check_node.jstree', (event, data) => {
            Ember.run(this, function() {
              if (this.get('isDestroyed') || this.get('isDestroying')) {
                return;
              }
              this.sendAction('eventDidCheckNode', data.node, data.selected, data.event);
            });
          });

          /*
             Event: uncheck_node.jstree
             Action: eventDidUncheckNode
             triggered when an node is unchecked (only if tie_selection in checkbox settings is false)
           */
          treeObject.on('uncheck_node.jstree', (event, data) => {
            Ember.run(this, function() {
              if (this.get('isDestroyed') || this.get('isDestroying')) {
                return;
              }
              this.sendAction('eventDidUncheckNode', data.node, data.selected, data.event);
            });
          });

          /*
             Event: check_all.jstree
             Action: eventDidCheckAll
             triggered when all nodes are checked (only if tie_selection in checkbox settings is false)
           */
          treeObject.on('check_all.jstree', (event, data) => {
            Ember.run(this, function() {
              if (this.get('isDestroyed') || this.get('isDestroying')) {
                return;
              }
              this.sendAction('eventDidCheckAll', data.selected);
            });
          });

          /*
             Event: uncheck_all.jstree
             Action: eventDidUncheckAll
             triggered when all nodes are unchecked (only if tie_selection in checkbox settings is false)
           */
          treeObject.on('uncheck_all.jstree', (event, data) => {
            Ember.run(this, function() {
              if (this.get('isDestroyed') || this.get('isDestroying')) {
                return;
              }
              this.sendAction('eventDidUncheckAll', data.node, data.selected);
            });
          });
        }
      }
    },

    /**
     * Refreshes the data in the tree
     * TODO: Investigate why redraw(true) doesn't work...
     *
     * @method _redrawTree
     */
    _refreshTree: Ember.observer('data', function() {
        let tree = this.getTree();
        if (null !== tree && false !== tree) {
            tree.settings.core['data'] = this.get('data');
            tree.refresh(this.get('skipLoading'), this.get('forgetState'));
        } else {
            // setup again if destroyed
            let treeObject = this._setupJsTree();
            this._setupEventHandlers(treeObject);
            this.set('treeObject', treeObject);
        }
    }),

    getTree() {
        let tree = this.get('treeObject');
        return tree.jstree(true);
    },

    actions: {

        contextmenuItemDidClick(actionName, data) {
            let emberTreeObj = this.get('getTree');

            let instance = Ember.$.jstree.reference(data.reference);
			let node = instance.get_node(data.reference);

            if (Ember.isPresent(actionName)) {
                this.sendAction(actionName, node, emberTreeObj);
            }
        }
    }

});
<commit_msg>remove commented code<commit_after>/*jshint loopfunc: true */

import Ember from 'ember';
import InboundActions from 'ember-component-inbound-actions/inbound-actions';
import EmberJstreeActions from 'ember-cli-jstree/mixins/ember-jstree-actions';

export default Ember.Component.extend(InboundActions, EmberJstreeActions, {

    // Properties for Ember communication
    actionReceiver:       null,
    currentNode:          null,
    selectedNodes:        null,

    // Basic configuration objects
    data:                 null,
    plugins:              null,
    themes:               null,
    checkCallback:        true,
    multiple:             true,

    // Refresh configuration variables
    skipLoading:          false,
    forgetState:          false,

    // Plugin option objects
    checkboxOptions:      null,
    contextmenuOptions:   null,
    typesOptions:         null,
    searchOptions:        null,
    dndOptions:           null,

    selectionDidChange:   null,
    treeObject:           null,

    // Internals
    _isDestroying:        false,

    isReady:              false,
    _searchTerm:          null,

    _isReadyTestWaiter() {
        return this.get('isReady') === true;
    },

    didInsertElement() {
        Ember.run.schedule('afterRender', this, this.createTree);
    },

    createTree() {
        if (Ember.testing) {
            // Add test waiter.
            Ember.Test.registerWaiter(this, this._isReadyTestWaiter);
        }

        let treeObject = this._setupJsTree();

        this._setupEventHandlers(treeObject);

        this.set('treeObject', treeObject);
    },

    willDestroyElement() {
        if (Ember.testing) {
            Ember.Test.unregisterWaiter(this, this._isReadyTestWaiter);
        }

        this.set('isReady', false);
        this.set('_isDestroying', true);
        this.send('destroy');
    },

    didUpdateAttrs() {
        this._super(...arguments);

        let pluginsArray = this.get('plugins');
        if (Ember.isPresent(pluginsArray)) {
            let searchOptions = this.get('searchOptions');
            if (Ember.isPresent(searchOptions) && pluginsArray.includes('search')) {
                let searchTerm = this.get('searchTerm');
                if (this.get('_searchTerm') !== searchTerm) {
                    Ember.run.next('afterRender', () => {
                        this.set('_searchTerm', searchTerm);
                        this.getTree().search(searchTerm);
                    });
                }
            }
        }
    },

    /**
     * Main setup function that registers all the plugins and sets up the core
     * configuration object for jsTree
     *
     * @method _setupJsTree
     */
    _setupJsTree() {
        return this.$().jstree(this._buildConfig());
    },

    /**
     * Builds config object for jsTree. Could be used to override config in descendant classes.
     *
     * @method _buildConfig
     */
    _buildConfig() {
        let configObject = {};

        configObject['core'] = {
            'data': this.get('data'),
            'check_callback': this.get('checkCallback'),
            'multiple': this.get('multiple')
        };

        let themes = this.get('themes');
        if (Ember.isPresent(themes) && Ember.typeOf(themes) === 'object') {
            configObject['core']['themes'] = themes;
        }

        let pluginsArray = this.get('plugins');
        if (Ember.isPresent(pluginsArray)) {
            pluginsArray = pluginsArray.replace(/ /g, '').split(',');
            configObject['plugins'] = pluginsArray;

            if (pluginsArray.includes('contextmenu') ||
                pluginsArray.includes('dnd') ||
                pluginsArray.includes('unique')) {
                // These plugins need core.check_callback
                configObject['core']['check_callback'] = configObject['core']['check_callback'] || true;
            }

            let checkboxOptions = this.get('checkboxOptions');
            if (Ember.isPresent(checkboxOptions) && pluginsArray.includes('checkbox')) {
                configObject['checkbox'] = checkboxOptions;
            }

            let searchOptions = this.get('searchOptions');
            if (Ember.isPresent(searchOptions) && pluginsArray.includes('search')) {
                configObject['search'] = searchOptions;
            }

            let stateOptions = this.get('stateOptions');
            if (Ember.isPresent(stateOptions) && pluginsArray.includes('state')) {
                configObject['state'] = stateOptions;
            }

            let typesOptions = this.get('typesOptions');
            if (Ember.isPresent(typesOptions) && pluginsArray.includes('types')) {
                configObject['types'] = typesOptions;
            }

            let contextmenuOptions = this.get('contextmenuOptions');
            if (Ember.isPresent(contextmenuOptions) && pluginsArray.includes('contextmenu')) {
                configObject['contextmenu'] = this._setupContextMenus(contextmenuOptions);
            }

            let dndOptions = this.get('dndOptions');
            if (Ember.isPresent(dndOptions) && pluginsArray.includes('dnd')) {
                configObject['dnd'] = dndOptions;
            }
        }

        return configObject;
    },

    /**
     * Setup context menu action handlers to handle ember actions
     *
     * @method _setupContextMenus
     * @param  {Array} contextmenuOptions Context menu configuration options
     * @return {Array} An Array of Ember-friendly options to pass back into the config object
     */
    _setupContextMenus(contextmenuOptions) {
        if (Ember.typeOf(contextmenuOptions['items']) === 'object') {
            let newMenuItems = {};
            let menuItems = Object.keys(contextmenuOptions['items']);
            for (let menuItem of menuItems) {
                let itemData = contextmenuOptions['items'][menuItem];
                newMenuItems[menuItem] = itemData;

                // Only change if not a function
                // This needs to be done to handle Ember actions
                if (Ember.typeOf(itemData['action']) !== 'function') {
                    let emberAction = itemData['action'];

                    newMenuItems[menuItem]['action'] = (data) => {
                        this.send("contextmenuItemDidClick", emberAction, data);
                    };
                }
            }

            contextmenuOptions['items'] = newMenuItems;
        }

        return contextmenuOptions;
    },

    /**
     * Register all sorts of events
     * TODO: This should eventually encompass all of the jsTree events declared in their API.
     *
     * @method _setupEventHandlers
     * @param  {Object}
     * @return
     */
    _setupEventHandlers(treeObject) {

        if (typeof treeObject !== 'object') {
            throw new Error('You must pass a valid jsTree object to set up its event handlers');
        }

        /*
          Event: init.jstree
          Action: eventDidInit
          triggered after all events are bound
        */
        treeObject.on('init.jstree', () => {
            Ember.run(this, function() {
                if (this.get('isDestroyed') || this.get('isDestroying')) {
                    return;
                }
                this.sendAction('eventDidInit');
            });
        });

        /*
          Event: loading.jstree
          Action: eventIsLoading
          triggered after the loading text is shown and before loading starts
        */
        treeObject.on('loading.jstree', () => {
            Ember.run(this, function() {
                if (this.get('isDestroyed') || this.get('isDestroying')) {
                    return;
                }
                this.sendAction('eventIsLoading');
            });
        });

        /*
          Event: loaded.jstree
          Action: eventDidLoad
          triggered after the root node is loaded for the first time
        */
        treeObject.on('loaded.jstree', () => {
            Ember.run(this, function() {
                if (this.get('isDestroyed') || this.get('isDestroying')) {
                    return;
                }
                this.sendAction('eventDidLoad');
            });
        });

        /*
          Event: ready.jstree
          Action: eventDidBecomeReady
          triggered after all nodes are finished loading
        */
        treeObject.on('ready.jstree', () => {
            Ember.run(this, function() {
                if (this.get('isDestroyed') || this.get('isDestroying')) {
                    return;
                }
                this.set('isReady', true);
                this.sendAction('eventDidBecomeReady');
            });
        });

        /*
          Event: redraw.jstree
          Action: eventDidRedraw
          triggered after nodes are redrawn
        */
        treeObject.on('redraw.jstree', () => {
            Ember.run(this, function() {
                if (this.get('isDestroyed') || this.get('isDestroying')) {
                    return;
                }
                this.sendAction('eventDidRedraw');
            });
        });

        /*
          Event: after_open.jstree
          Action: eventDidOpen
          triggered when a node is opened and the animation is complete
        */
        treeObject.on('after_open.jstree', (event, data) => {
            Ember.run(this, function() {
                if (this.get('isDestroyed') || this.get('isDestroying')) {
                    return;
                }
                this.sendAction('eventDidOpen', data.node);
            });
        });

        /*
          Event: after_close.jstree
          Action: eventDidClose
          triggered when a node is closed and the animation is complete
        */
        treeObject.on('after_close.jstree', (event, data) => {
            Ember.run(this, function() {
                if (this.get('isDestroyed') || this.get('isDestroying')) {
                    return;
                }
                this.sendAction('eventDidClose', data.node);
            });
        });

        /*
          Event: select_node.jstree
          Action: eventDidSelectNode
          triggered when an node is selected
        */
        treeObject.on('select_node.jstree', (event, data) => {
            Ember.run(this, function() {
                if (this.get('isDestroyed') || this.get('isDestroying')) {
                    return;
                }
                this.sendAction('eventDidSelectNode', data.node, data.selected, data.event);
            });
        });

        /*
          Event: deselect_node.jstree
          Action: eventDidDeelectNode
          triggered when an node is deselected
        */
        treeObject.on('deselect_node.jstree', (event, data) => {
            Ember.run(this, function() {
                if (this.get('isDestroyed') || this.get('isDestroying')) {
                    return;
                }
                this.sendAction('eventDidDeselectNode', data.node, data.selected, data.event);
            });
        });

        /*
          Event: changed.jstree
          Action: jstreeDidChange
          triggered when selection changes
        */
        treeObject.on('changed.jstree', (event, data) => {
            Ember.run(this, function() {
                if (this.get('isDestroyed') || this.get('isDestroying')) {
                    return;
                }

                // Check if selection changed
                if (Ember.isPresent(this.get('treeObject'))) {
                    let selectionChangedEventNames = [
                        'model',
                        'select_node',
                        'deselect_node',
                        'select_all',
                        'deselect_all'
                    ];
                    if (Ember.isPresent(data.action) && selectionChangedEventNames.includes(data.action)) {
                        let selNodes = Ember.A(this.get('treeObject').jstree(true).get_selected(true));
                        this.set('selectedNodes', selNodes);
                    }
                }

                this.sendAction('eventDidChange', data);
            });
        });

        /*
          Event: hover_node.jstree
          Action: eventDidHoverNode
          triggered when a node is hovered
        */
        treeObject.on('hover_node.jstree', (event, data) => {
            Ember.run(this, function() {
                if (this.get('isDestroyed') || this.get('isDestroying')) {
                    return;
                }
                this.sendAction('eventDidHoverNode', data.node);
            });
        });

        /*
          Event: dehover_node.jstree
          Action: eventDidDehoverNode
          triggered when a node is no longer hovered
        */
        treeObject.on('dehover_node.jstree', (event, data) => {
            Ember.run(this, function() {
                if (this.get('isDestroyed') || this.get('isDestroying')) {
                    return;
                }
                this.sendAction('eventDidDehoverNode', data.node);
            });
        });

        /*
          Event: show_node.jstree
          Action: eventDidShowNode
          triggered when a node is no longer hovered
        */
        treeObject.on('show_node.jstree', (event, data) => {
            Ember.run(this, function() {
                if (this.get('isDestroyed') || this.get('isDestroying')) {
                    return;
                }
                this.sendAction('eventDidShowNode', data.node);
            });
        });

        /*
          Event: move_node.jstree
          Action: eventDidMoveNode
          triggered when a node is moved
        */
        treeObject.on('move_node.jstree', (event, data) => {
            Ember.run(this, function() {
                if (this.get('isDestroyed') || this.get('isDestroying')) {
                    return;
                }
                this.sendAction('eventDidMoveNode', data);
            });
        });

      if (this.get('plugins').indexOf('checkbox') > -1) {

        /*
           Event: disable_checkbox.jstree
           Action: eventDidDisableCheckbox
           triggered when an node's checkbox is disabled
         */
        treeObject.on('disable_checkbox.jstree', (event, data) => {
          Ember.run(this, function() {
            if (this.get('isDestroyed') || this.get('isDestroying')) {
              return;
            }
            this.sendAction('eventDidDisableCheckbox', data.node);
          });
        });

        /*
           Event: enable_checkbox.jstree
           Action: eventDidEnableCheckbox
           triggered when an node's checkbox is enabled
         */
        treeObject.on('enable_checkbox.jstree', (event, data) => {
          Ember.run(this, function() {
            if (this.get('isDestroyed') || this.get('isDestroying')) {
              return;
            }
            this.sendAction('eventDidEnableCheckbox', data.node);
          });
        });

        if (Ember.isPresent('checkboxOptions.tie_selected') && !this.get('checkboxOptions.tie_selected')) {

          /*
             Event: check_node.jstree
             Action: eventDidCheckNode
             triggered when an node is checked (only if tie_selection in checkbox settings is false)
           */
          treeObject.on('check_node.jstree', (event, data) => {
            Ember.run(this, function() {
              if (this.get('isDestroyed') || this.get('isDestroying')) {
                return;
              }
              this.sendAction('eventDidCheckNode', data.node, data.selected, data.event);
            });
          });

          /*
             Event: uncheck_node.jstree
             Action: eventDidUncheckNode
             triggered when an node is unchecked (only if tie_selection in checkbox settings is false)
           */
          treeObject.on('uncheck_node.jstree', (event, data) => {
            Ember.run(this, function() {
              if (this.get('isDestroyed') || this.get('isDestroying')) {
                return;
              }
              this.sendAction('eventDidUncheckNode', data.node, data.selected, data.event);
            });
          });

          /*
             Event: check_all.jstree
             Action: eventDidCheckAll
             triggered when all nodes are checked (only if tie_selection in checkbox settings is false)
           */
          treeObject.on('check_all.jstree', (event, data) => {
            Ember.run(this, function() {
              if (this.get('isDestroyed') || this.get('isDestroying')) {
                return;
              }
              this.sendAction('eventDidCheckAll', data.selected);
            });
          });

          /*
             Event: uncheck_all.jstree
             Action: eventDidUncheckAll
             triggered when all nodes are unchecked (only if tie_selection in checkbox settings is false)
           */
          treeObject.on('uncheck_all.jstree', (event, data) => {
            Ember.run(this, function() {
              if (this.get('isDestroyed') || this.get('isDestroying')) {
                return;
              }
              this.sendAction('eventDidUncheckAll', data.node, data.selected);
            });
          });
        }
      }
    },

    /**
     * Refreshes the data in the tree
     * TODO: Investigate why redraw(true) doesn't work...
     *
     * @method _redrawTree
     */
    _refreshTree: Ember.observer('data', function() {
        let tree = this.getTree();
        if (null !== tree && false !== tree) {
            tree.settings.core['data'] = this.get('data');
            tree.refresh(this.get('skipLoading'), this.get('forgetState'));
        } else {
            // setup again if destroyed
            let treeObject = this._setupJsTree();
            this._setupEventHandlers(treeObject);
            this.set('treeObject', treeObject);
        }
    }),

    getTree() {
        let tree = this.get('treeObject');
        return tree.jstree(true);
    },

    actions: {

        contextmenuItemDidClick(actionName, data) {
            let emberTreeObj = this.get('getTree');

            let instance = Ember.$.jstree.reference(data.reference);
			let node = instance.get_node(data.reference);

            if (Ember.isPresent(actionName)) {
                this.sendAction(actionName, node, emberTreeObj);
            }
        }
    }

});
<|endoftext|>


<commit_before>"use strict"

import EventEmitter from "events"

/* Dynamically creates a ChatBox with a textarea and an text input field. */
class ChatBox extends EventEmitter {

  constructor(rootNodeId) {
    super()
    let rootNode = document.getElementById(rootNodeId)
    if (!rootNode) { throw new Error("ChatBox: can't find element by id '" + rootNodeId + "'.") }

    // create elements
    let chat_list_div = document.createElement("div")
    chat_list_div.className = "chat_box_div"
    rootNode.appendChild(chat_list_div)
    this.chat_list_div = chat_list_div

    let chat_list = document.createElement("ul")
    chat_list.className = "chat_box_list"
    chat_list_div.appendChild(chat_list)
    this.chat_list = chat_list

    let chat_input = document.createElement("input")
    chat_input.className = "chat_box_input"
    rootNode.appendChild(chat_input)

    chat_input.addEventListener("keydown", (e) => {
      if (!e) { var e = window.event }
      if (e.keyCode == 13 && chat_input.value != "") {
        this.emit("input", chat_input.value)
        chat_input.value = ""
      }
    })
  }

  addMessage(msg) {
    if (msg.type === undefined) { msg.type = "chat" }
    let li = document.createElement("li")
    li.appendChild(document.createTextNode((msg.name ? msg.name + ": " : "") + msg.data))
    li.className = msg.type
    this.chat_list.appendChild(li)
    this.chat_list_div.scrollTop = this.chat_list_div.scrollHeight
  }

  addSystemMessage(msg) {
    this.addMessage({type: "system", data: msg})
  }
}

export default ChatBox
<commit_msg>autoselect text input<commit_after>"use strict"

import EventEmitter from "events"

/* Dynamically creates a ChatBox with a textarea and an text input field. */
class ChatBox extends EventEmitter {

  constructor(rootNodeId) {
    super()
    let rootNode = document.getElementById(rootNodeId)
    if (!rootNode) { throw new Error("ChatBox: can't find element by id '" + rootNodeId + "'.") }

    // create elements
    let chat_list_div = document.createElement("div")
    chat_list_div.className = "chat_box_div"
    rootNode.appendChild(chat_list_div)
    this.chat_list_div = chat_list_div

    let chat_list = document.createElement("ul")
    chat_list.className = "chat_box_list"
    chat_list_div.appendChild(chat_list)
    this.chat_list = chat_list

    let chat_input = document.createElement("input")
    chat_input.className = "chat_box_input"
    rootNode.appendChild(chat_input)
    chat_input.select()

    chat_input.addEventListener("keydown", (e) => {
      if (!e) { var e = window.event }
      if (e.keyCode == 13 && chat_input.value != "") {
        this.emit("input", chat_input.value)
        chat_input.value = ""
      }
    })
  }

  addMessage(msg) {
    if (msg.type === undefined) { msg.type = "chat" }
    let li = document.createElement("li")
    li.appendChild(document.createTextNode((msg.name ? msg.name + ": " : "") + msg.data))
    li.className = msg.type
    this.chat_list.appendChild(li)
    this.chat_list_div.scrollTop = this.chat_list_div.scrollHeight
  }

  addSystemMessage(msg) {
    this.addMessage({type: "system", data: msg})
  }
}

export default ChatBox
<|endoftext|>


<commit_before>import Ember from 'ember';

export default Ember.TextField.extend({

  mask: "9999 9999",
  tagName: "input",
  maskholder: "-",
  autoclear: false,

  didInsertElement() {
    this.$().mask(this.get("mask"), {
      placeholder: this.get("maskholder"),
      autoclear: this.get("autoclear"),
      completed: this.completed.bind(this)
    });
  },

  completed() {
    this.sendAction("inputReady");
  }
});
<commit_msg>call this._super in didInsertElement<commit_after>import Ember from 'ember';

export default Ember.TextField.extend({
  mask: "9999 9999",
  tagName: "input",
  maskholder: "-",
  autoclear: false,

  didInsertElement() {
    this._super(...arguments);
    this.$().mask(this.get("mask"), {
      placeholder: this.get("maskholder"),
      autoclear: this.get("autoclear"),
      completed: this.completed.bind(this)
    });
  },

  completed() {
    this.sendAction("inputReady");
  }
});
<|endoftext|>


<commit_before>/*!
 * ChatWork - JavaScript SDK for ChatWork API v1 Preview
 *
 * Copyright 2013, Tetsuwo OISHI.
 * Dual license under the MIT license.
 * http://tetsuwo.tumblr.com
 *
 * Version: 0.0.1
 * Date: 2013-11-29
 */

function ChatWork(param) {
    this.initialize.apply(this, arguments);
}

ChatWork.prototype.initialize = function(param) {
    this.apiBaseUrl = 'https://api.chatwork.com/v1';
    this.name       = '_chatwork';
    this.times      = 0;
    this.requests   = [];
    this.data       = [];
    this.debug      = false;
    this.win        = document.defaultView || document.parentWindow;

    this.config = {
        apiToken: null
    };

    if (param) {
        if (param.debug) {
            this.debug = param.debug;
        }
        if (param.apiKey) {
            this.config.apiToken = param.apiToken;
        }
    }

    this.output(param);
};

ChatWork.prototype.setApiToken = function(val) {
    this.config.apiToken = val;
    return this;
};

ChatWork.prototype.output = function(val) {
    if (this.debug) {
        console.log(val);
    }
};

ChatWork.prototype.serialize = function(param, prefix) {
    var query = [];

    for(var p in param) {
        var k = prefix ? prefix + '[' + p + ']' : p, v = param[p];
        query.push(
            typeof v == 'object' ?
                this.serialize(v, k) :
                encodeURIComponent(k) + '=' + encodeURIComponent(v)
        );
    }

    return query.join('&');
};

ChatWork.prototype.api = function(method, param, callback) {
    var callbackName = this.name + '_cb_' + this.times;
    this.win[callbackName] = callback;

    param = param || {};
    param.api_key = this.config.apiKey;
    param.jsonp   = callbackName;

    this.requests[this.times] = {
        method: method,
        param: param,
        callback: callback,
        callbackName: callbackName
    };

    if (param.limit === 'all') {
        param.limit = null;
    }

    this.times++;

    (function(that, d, t) {
        var e = d.createElement(t);
        e.type = 'text/javascript';
        e.async = true;
        e.src = that.apiUrl;
        e.src += method;
        e.src += '?' + that.serialize(param);
        that.output(e.src);
        var s = d.getElementsByTagName(t)[0];
        s.parentNode.insertBefore(e, s);
    })(this, document, 'script');

    return this;
};

<commit_msg>Update chatwork.js<commit_after>/*!
 * ChatWork - Chrome JavaScript SDK for ChatWork API v1 Preview
 *
 * Copyright 2013, Tetsuwo OISHI.
 * Dual license under the MIT license.
 * http://tetsuwo.tumblr.com
 *
 * Version: 0.0.1
 * Date: 2013-11-29
 */

function ChatWork(param) {
    this.initialize.apply(this, arguments);
}

/**
 * Initialize
 *
 * @param {object} param
 */
ChatWork.prototype.initialize = function(param) {
    this.apiBaseUrl = 'https://api.chatwork.com/v1';
    this.url        = 'https://chatwork.com';
    this.name       = '_chatwork';
    this.times      = 0;
    this.requests   = [];
    this.data       = [];
    this.debug      = false;
    this.win        = document.defaultView || document.parentWindow;

    this.config = {
        apiToken: null
    };

    if (param) {
        if (param.debug) {
            this.debug = param.debug;
        }
        if (param.apiKey) {
            this.config.apiToken = param.apiToken;
        }
    }

    this.log(param);
};

/**
 * Set API Token
 *
 * @param {string} apiToken
 */
ChatWork.prototype.setApiToken = function(apiToken) {
    this.config.apiToken = apiToken;
    return this;
};

/**
 * Logging
 *
 */
ChatWork.prototype.log = function() {
    if (this.debug) {
        console.log(arguments);
    }
};

/**
 * Serialize parameters
 *
 * @param {object} param
 * @param {string} prefix
 */
ChatWork.prototype.serialize = function(param, prefix) {
    var query = [];
    for (var p in param) {
        var k = prefix ? prefix + '[' + p + ']' : p, v = param[p];
        query.push(
            typeof v == 'object' ?
                this.serialize(v, k) :
                encodeURIComponent(k) + '=' + encodeURIComponent(v)
        );
    }
    return query.join('&');
};

/**
 * API Caller
 *
 * @param {string} method
 * @param {object} param
 * @param {function} callback
 */
ChatWork.prototype.api = function(method, param, callback) {
    var endpoint = this.apiBaseUrl + method;
    try {
        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function() {
            if (xhr.readyState == 4 && xhr.status == 200) {
                var response = JSON.parse(xhr.responseText);
                callback(response);
            }
        };
        xhr.open('GET', endpoint);
        xhr.setRequestHeader('X-ChatWorkToken', this.config.apiToken);
        xhr.send();
    } catch (ex) {
        this.log('xhr.exception', ex);
    }
    return this;
};
<|endoftext|>


<commit_before>import Ember from 'ember';
import noUiSlider from 'noUiSlider';

const {
  on,
  run,
  isEmpty,
  computed,
  observer
} = Ember;

export default Ember.Component.extend({
  attributeBindings: ['disabledOrUndefined:disabled'],
  slider:       null,
  start:        undefined,
  step:         undefined,
  margin:       undefined,
  limit:        undefined,
  pips:         undefined,
  animate:      true,
  snap:         false,
  connect:      false,
  disabled:     false,
  orientation:  'horizontal',
  direction:    'ltr',
  behaviour:    'tap',
  tooltips:     false,

  min: 0,
  max: 100,

  range: computed('min', 'max', function() {
    return {
      min: this.get('min'),
      max: this.get('max')
    };
  }),

  formatTo(value) { return value; },
  formatFrom(value) { return value; },

  format: computed('formatTo', 'formatFrom', function() {
    return {
      to: this.get('formatTo'),
      from: this.get('formatFrom')
    };
  }),

  setup: on('didInsertElement', function() {
    let $this = this.$().get(0);
    let properties = this.getProperties(
      'start', 'step', 'margin',
      'limit', 'range', 'connect',
      'orientation', 'direction',
      'behaviour', 'animate', 'snap',
      'pips', 'format', 'tooltips'
    );
    let sliderEvents = Ember.A(['change', 'set', 'slide', 'update', 'start', 'end']);

    // We first check if the element has a slider already created
    if ($this.noUiSlider && $this.noUiSlider.destroy) {
      $this.noUiSlider.destroy();
    }

    noUiSlider.create($this, properties, true);

    let slider = $this.noUiSlider;

    // We set slider next sync cycle to avoid deprecation warnings
    run.schedule('sync', () => {
      this.set('slider', slider);

      sliderEvents.forEach(event => {
        if (!isEmpty(this.get(`on-${event}`))) {
          slider.on(event, () => {
            run(this, function() {
              let val = this.get("slider").get();
              this.sendAction(`on-${event}`, val);
            });
          });
        }
      });

      /** DEPRECATED AND WILL BE REMOVED BEFORE 1.0 **/
      slider.on('change', () => {
        run(this, function () {
            let val = this.get("slider").get();
            this.sendDeprecatedAction("change", val);
        });
      });

      if (!isEmpty(this.get('slide'))) {
        slider.on('slide', () => {
          run(this, function () {
            let val = this.get("slider").get();
            this.sendDeprecatedAction('slide', val);
          });
        });
      }
    });

  }),

  update: on('didUpdateAttrs', function() {
    let slider = this.get('slider');
    let properties = this.getProperties(
      'margin', 'limit', 'step',
      'range', 'animate', 'snap',
      'start'
    );

    if (slider) {
      slider.updateOptions(properties);
    }
  }),

  teardown: on('willDestroyElement', function() {
    var slider = this.get('slider');

    slider.off('change');
    slider.off('slide');
    slider.off('set');
    slider.off('update');
    slider.off('start');
    slider.off('end');

    slider.destroy();
  }),

  setVal: observer('start', function() {
    let slider = this.get('slider');

    if (slider) {
      var val = this.get('start');
      slider.set(val);
    }
  }),

  // disabled can't be just `false` - this leads to an attribute of disabled="false"
  disabledOrUndefined: Ember.computed('disabled', function() {
    if (this.get('disabled')) {
      return true;
    }
  }),
  /**
   * Perform a naive check to see if the deprecated action name exists in our
   * attrs and then log a deprecation warning and trigger the old action.
   */
  sendDeprecatedAction(action, value) {
    var actionName = this.get(`attrs.${action}`);
    if(!isEmpty(actionName)) {
      Ember.Logger.warn(`DEPRECATION (ember-cli-nouislider): "${action}" action is deprecated in favor of "on-${action}". Support for "${action}" will be dropped in 1.0`);
      this.sendAction(action, value);
    }
  }
});
<commit_msg>space deletion<commit_after>import Ember from 'ember';
import noUiSlider from 'noUiSlider';

const {
  on,
  run,
  isEmpty,
  computed,
  observer
} = Ember;

export default Ember.Component.extend({
  attributeBindings: ['disabledOrUndefined:disabled'],
  slider:       null,
  start:        undefined,
  step:         undefined,
  margin:       undefined,
  limit:        undefined,
  pips:         undefined,
  animate:      true,
  snap:         false,
  connect:      false,
  disabled:     false,
  orientation:  'horizontal',
  direction:    'ltr',
  behaviour:    'tap',
  tooltips:     false,

  min: 0,
  max: 100,

  range: computed('min', 'max', function() {
    return {
      min: this.get('min'),
      max: this.get('max')
    };
  }),

  formatTo(value) { return value; },
  formatFrom(value) { return value; },

  format: computed('formatTo', 'formatFrom', function() {
    return {
      to: this.get('formatTo'),
      from: this.get('formatFrom')
    };
  }),

  setup: on('didInsertElement', function() {
    let $this = this.$().get(0);
    let properties = this.getProperties(
      'start', 'step', 'margin',
      'limit', 'range', 'connect',
      'orientation', 'direction',
      'behaviour', 'animate', 'snap',
      'pips', 'format', 'tooltips'
    );
    let sliderEvents = Ember.A(['change', 'set', 'slide', 'update', 'start', 'end']);

    // We first check if the element has a slider already created
    if ($this.noUiSlider && $this.noUiSlider.destroy) {
      $this.noUiSlider.destroy();
    }

    noUiSlider.create($this, properties, true);

    let slider = $this.noUiSlider;

    // We set slider next sync cycle to avoid deprecation warnings
    run.schedule('sync', () => {
      this.set('slider', slider);

      sliderEvents.forEach(event => {
        if (!isEmpty(this.get(`on-${event}`))) {
          slider.on(event, () => {
            run(this, function() {
              let val = this.get("slider").get();
              this.sendAction(`on-${event}`, val);
            });
          });
        }
      });

      /** DEPRECATED AND WILL BE REMOVED BEFORE 1.0 **/
      slider.on('change', () => {
        run(this, function () {
            let val = this.get("slider").get();
            this.sendDeprecatedAction("change", val);
        });
      });

      if (!isEmpty(this.get('slide'))) {
        slider.on('slide', () => {
          run(this, function () {
            let val = this.get("slider").get();
            this.sendDeprecatedAction('slide', val);
          });
        });
      }
    });
  }),

  update: on('didUpdateAttrs', function() {
    let slider = this.get('slider');
    let properties = this.getProperties(
      'margin', 'limit', 'step',
      'range', 'animate', 'snap',
      'start'
    );

    if (slider) {
      slider.updateOptions(properties);
    }
  }),

  teardown: on('willDestroyElement', function() {
    var slider = this.get('slider');

    slider.off('change');
    slider.off('slide');
    slider.off('set');
    slider.off('update');
    slider.off('start');
    slider.off('end');

    slider.destroy();
  }),

  setVal: observer('start', function() {
    let slider = this.get('slider');

    if (slider) {
      var val = this.get('start');
      slider.set(val);
    }
  }),

  // disabled can't be just `false` - this leads to an attribute of disabled="false"
  disabledOrUndefined: Ember.computed('disabled', function() {
    if (this.get('disabled')) {
      return true;
    }
  }),
  /**
   * Perform a naive check to see if the deprecated action name exists in our
   * attrs and then log a deprecation warning and trigger the old action.
   */
  sendDeprecatedAction(action, value) {
    var actionName = this.get(`attrs.${action}`);
    if(!isEmpty(actionName)) {
      Ember.Logger.warn(`DEPRECATION (ember-cli-nouislider): "${action}" action is deprecated in favor of "on-${action}". Support for "${action}" will be dropped in 1.0`);
      this.sendAction(action, value);
    }
  }
});
<|endoftext|>